From 60dcffb8564bfcc1401ae4c934c742e31a24bc2f Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 24 Oct 2024 11:03:32 +0100 Subject: [PATCH 001/404] fix: internally wrap store subscribe in untrack (#13858) * fix: internally wrap store subscribe in untrack * lint * Update packages/svelte/src/store/utils.js Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --------- Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --- .changeset/little-hotels-poke.md | 5 +++++ packages/svelte/src/store/utils.js | 12 ++++++++---- .../main.svelte | 5 +---- 3 files changed, 14 insertions(+), 8 deletions(-) create mode 100644 .changeset/little-hotels-poke.md diff --git a/.changeset/little-hotels-poke.md b/.changeset/little-hotels-poke.md new file mode 100644 index 0000000000..50b60e2142 --- /dev/null +++ b/.changeset/little-hotels-poke.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: internally wrap store subscribe in untrack diff --git a/packages/svelte/src/store/utils.js b/packages/svelte/src/store/utils.js index d48036ea67..db2a62c68c 100644 --- a/packages/svelte/src/store/utils.js +++ b/packages/svelte/src/store/utils.js @@ -1,4 +1,5 @@ /** @import { Readable } from './public' */ +import { untrack } from '../index-client.js'; import { noop } from '../internal/shared/utils.js'; /** @@ -20,10 +21,13 @@ export function subscribe_to_store(store, run, invalidate) { } // Svelte store takes a private second argument - const unsub = store.subscribe( - run, - // @ts-expect-error - invalidate + // StartStopNotifier could mutate state, and we want to silence the corresponding validation error + const unsub = untrack(() => + store.subscribe( + run, + // @ts-expect-error + invalidate + ) ); // Also support RxJS diff --git a/packages/svelte/tests/runtime-runes/samples/store-unsubscribe-not-referenced-after/main.svelte b/packages/svelte/tests/runtime-runes/samples/store-unsubscribe-not-referenced-after/main.svelte index e097c639f0..5f8b19f4c7 100644 --- a/packages/svelte/tests/runtime-runes/samples/store-unsubscribe-not-referenced-after/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/store-unsubscribe-not-referenced-after/main.svelte @@ -1,5 +1,4 @@ + + From 04c38b089fb3aa99d2394c86a7dc2c26907cb635 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Thu, 24 Oct 2024 14:21:27 +0200 Subject: [PATCH 003/404] fix: allow binding to const with spread in legacy mode (#13849) --- .changeset/nine-pigs-approve.md | 5 +++++ .../svelte/src/internal/client/reactivity/props.js | 13 +++++++++++++ packages/svelte/src/internal/server/index.js | 7 ++++++- .../bind-export-const-with-spread/Test.svelte | 3 +++ .../bind-export-const-with-spread/_config.js | 9 +++++++++ .../bind-export-const-with-spread/main.svelte | 11 +++++++++++ 6 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 .changeset/nine-pigs-approve.md create mode 100644 packages/svelte/tests/runtime-legacy/samples/bind-export-const-with-spread/Test.svelte create mode 100644 packages/svelte/tests/runtime-legacy/samples/bind-export-const-with-spread/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/bind-export-const-with-spread/main.svelte diff --git a/.changeset/nine-pigs-approve.md b/.changeset/nine-pigs-approve.md new file mode 100644 index 0000000000..b11daff5a3 --- /dev/null +++ b/.changeset/nine-pigs-approve.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: allow binding to const with spread in legacy mode diff --git a/packages/svelte/src/internal/client/reactivity/props.js b/packages/svelte/src/internal/client/reactivity/props.js index 1d0f7b239f..1df1933c19 100644 --- a/packages/svelte/src/internal/client/reactivity/props.js +++ b/packages/svelte/src/internal/client/reactivity/props.js @@ -177,6 +177,19 @@ const spread_props_handler = { if (typeof p === 'object' && p !== null && key in p) return p[key]; } }, + set(target, key, value) { + let i = target.props.length; + while (i--) { + let p = target.props[i]; + if (is_function(p)) p = p(); + const desc = get_descriptor(p, key); + if (desc && desc.set) { + desc.set(value); + return true; + } + } + return false; + }, getOwnPropertyDescriptor(target, key) { let i = target.props.length; while (i--) { diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index 9b5e4ac3e2..9a66095de4 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -270,7 +270,12 @@ export function spread_props(props) { for (let i = 0; i < props.length; i++) { const obj = props[i]; for (key in obj) { - merged_props[key] = obj[key]; + const desc = Object.getOwnPropertyDescriptor(obj, key); + if (desc) { + Object.defineProperty(merged_props, key, desc); + } else { + merged_props[key] = obj[key]; + } } } return merged_props; diff --git a/packages/svelte/tests/runtime-legacy/samples/bind-export-const-with-spread/Test.svelte b/packages/svelte/tests/runtime-legacy/samples/bind-export-const-with-spread/Test.svelte new file mode 100644 index 0000000000..1ec9031319 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/bind-export-const-with-spread/Test.svelte @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/packages/svelte/tests/runtime-legacy/samples/bind-export-const-with-spread/_config.js b/packages/svelte/tests/runtime-legacy/samples/bind-export-const-with-spread/_config.js new file mode 100644 index 0000000000..0645e5bc7f --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/bind-export-const-with-spread/_config.js @@ -0,0 +1,9 @@ +import { test } from '../../test'; + +export default test({ + html: `

42

`, + async test({ target, assert }) { + const p = target.querySelector('p'); + assert.equal(p?.innerHTML, '42'); + } +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/bind-export-const-with-spread/main.svelte b/packages/svelte/tests/runtime-legacy/samples/bind-export-const-with-spread/main.svelte new file mode 100644 index 0000000000..8a0a370acb --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/bind-export-const-with-spread/main.svelte @@ -0,0 +1,11 @@ + + + +

{x}

\ No newline at end of file From b979c291e274e730a9d55f758ca463b65a09eb7b Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Thu, 24 Oct 2024 15:23:58 +0200 Subject: [PATCH 004/404] fix: properly traverse children when checking matches for `:has` (#13866) The previous algorithm didn't take control flow blocks etc into account, this fixes that. Fixes #13860 --- .changeset/tasty-students-peel.md | 5 ++ .../phases/2-analyze/css/css-prune.js | 33 +++++--- .../svelte/tests/css/samples/has/_config.js | 78 +++++++++++-------- .../svelte/tests/css/samples/has/expected.css | 6 ++ .../svelte/tests/css/samples/has/input.svelte | 9 +++ 5 files changed, 87 insertions(+), 44 deletions(-) create mode 100644 .changeset/tasty-students-peel.md diff --git a/.changeset/tasty-students-peel.md b/.changeset/tasty-students-peel.md new file mode 100644 index 0000000000..aa7c5472c5 --- /dev/null +++ b/.changeset/tasty-students-peel.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: properly traverse children when checking matches for `:has` 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 f5f7440085..f71a860958 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 @@ -405,19 +405,28 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element, /** @type {Array} */ const descendant_elements = []; - /** - * @param {Compiler.SvelteNode} node - * @param {boolean} is_recursing - */ - function collect_child_elements(node, is_recursing) { - if (node.type === 'RegularElement' || node.type === 'SvelteElement') { - descendant_elements.push(node); - if (!is_recursing) child_elements.push(node); - node.fragment.nodes.forEach((node) => collect_child_elements(node, true)); + walk( + /** @type {Compiler.SvelteNode} */ (element.fragment), + { is_child: true }, + { + _(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 { + context.next(); + } + } } - } - - element.fragment.nodes.forEach((node) => collect_child_elements(node, false)); + ); // :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 diff --git a/packages/svelte/tests/css/samples/has/_config.js b/packages/svelte/tests/css/samples/has/_config.js index 815e48e096..675254c5b7 100644 --- a/packages/svelte/tests/css/samples/has/_config.js +++ b/packages/svelte/tests/css/samples/has/_config.js @@ -6,112 +6,126 @@ export default test({ code: 'css_unused_selector', message: 'Unused CSS selector ".unused:has(y)"', start: { - line: 28, + line: 31, column: 1, - character: 277 + character: 308 }, end: { - line: 28, + line: 31, column: 15, - character: 291 + character: 322 } }, { code: 'css_unused_selector', message: 'Unused CSS selector ".unused:has(:global(y))"', start: { - line: 31, + line: 34, column: 1, - character: 312 + character: 343 }, end: { - line: 31, + line: 34, column: 24, - character: 335 + character: 366 } }, { code: 'css_unused_selector', message: 'Unused CSS selector "x:has(.unused)"', start: { - line: 34, + line: 37, column: 1, - character: 356 + character: 387 }, end: { - line: 34, + line: 37, column: 15, - character: 370 + character: 401 } }, { code: 'css_unused_selector', message: 'Unused CSS selector "x:has(y):has(.unused)"', start: { - line: 47, + line: 50, column: 1, - character: 525 + character: 556 }, end: { - line: 47, + line: 50, column: 22, - character: 546 + character: 577 } }, { code: 'css_unused_selector', message: 'Unused CSS selector ".unused"', start: { - line: 66, + line: 69, column: 2, - character: 751 + character: 782 }, end: { - line: 66, + line: 69, column: 9, - character: 758 + character: 789 } }, { code: 'css_unused_selector', message: 'Unused CSS selector ".unused x:has(y)"', start: { - line: 82, + line: 85, column: 1, - character: 905 + character: 936 }, end: { - line: 82, + line: 85, column: 17, - character: 921 + character: 952 } }, { code: 'css_unused_selector', message: 'Unused CSS selector ".unused:has(.unused)"', start: { - line: 85, + line: 88, column: 1, - character: 942 + character: 973 }, end: { - line: 85, + line: 88, column: 21, - character: 962 + character: 993 } }, { code: 'css_unused_selector', message: 'Unused CSS selector "x:has(> z)"', start: { - line: 92, + line: 98, + column: 1, + character: 1093 + }, + end: { + line: 98, + column: 11, + character: 1103 + } + }, + { + code: 'css_unused_selector', + message: 'Unused CSS selector "x:has(> d)"', + start: { + line: 101, column: 1, - character: 1029 + character: 1124 }, end: { - line: 92, + line: 101, column: 11, - character: 1039 + character: 1134 } } ] diff --git a/packages/svelte/tests/css/samples/has/expected.css b/packages/svelte/tests/css/samples/has/expected.css index 215f7d667e..251a6952d5 100644 --- a/packages/svelte/tests/css/samples/has/expected.css +++ b/packages/svelte/tests/css/samples/has/expected.css @@ -82,9 +82,15 @@ x.svelte-xyz:has(> y:where(.svelte-xyz)) { color: green; } + y.svelte-xyz:has(> d:where(.svelte-xyz)) { + color: green; + } /* (unused) x:has(> z) { color: red; }*/ + /* (unused) x:has(> d) { + color: red; + }*/ x.svelte-xyz > y:where(.svelte-xyz):has(z: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 7cbebe27ec..7a62b69c0b 100644 --- a/packages/svelte/tests/css/samples/has/input.svelte +++ b/packages/svelte/tests/css/samples/has/input.svelte @@ -1,6 +1,9 @@ + {#if foo} + + {/if} @@ -89,9 +92,15 @@ x:has(> y) { color: green; } + y:has(> d) { + color: green; + } x:has(> z) { color: red; } + x:has(> d) { + color: red; + } x > y:has(z) { color: green; } From 7c7fd6518f2c8d88a7c1a401a75bf97ab65c6cc8 Mon Sep 17 00:00:00 2001 From: adiGuba Date: Thu, 24 Oct 2024 19:20:40 +0200 Subject: [PATCH 005/404] feat: Little improvement to set_style() (#13836) * improvement to set_style() * add __styles to Element prototype --- .../3-transform/client/visitors/RegularElement.js | 8 +------- .../3-transform/client/visitors/SvelteElement.js | 2 +- .../3-transform/client/visitors/shared/element.js | 7 ++----- .../internal/client/dom/elements/attributes.js | 9 +++++++++ .../src/internal/client/dom/elements/style.js | 15 ++++++--------- .../svelte/src/internal/client/dom/operations.js | 2 ++ 6 files changed, 21 insertions(+), 22 deletions(-) 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 86707b1a23..d748fd85e8 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 @@ -291,13 +291,7 @@ export function RegularElement(node, context) { // 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); - build_style_directives( - style_directives, - node_id, - context, - is_attributes_reactive, - lookup.has('style') || has_spread - ); + build_style_directives(style_directives, node_id, context, is_attributes_reactive); // Apply the src and loading attributes for elements after the element is appended to the document if (node.name === 'img' && (has_spread || lookup.has('loading'))) { 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 8bae80be37..d95f03d1d4 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 @@ -109,7 +109,7 @@ export function SvelteElement(node, context) { // 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); - build_style_directives(style_directives, element_id, inner_context, is_attributes_reactive, true); + 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 1d0705b88d..e0fbe11b73 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 @@ -94,14 +94,12 @@ export function build_set_attributes( * @param {Identifier} element_id * @param {ComponentContext} context * @param {boolean} is_attributes_reactive - * @param {boolean} force_check Should be `true` if we can't rely on our cached value, because for example there's also a `style` attribute */ export function build_style_directives( style_directives, element_id, context, - is_attributes_reactive, - force_check + is_attributes_reactive ) { const state = context.state; @@ -126,8 +124,7 @@ export function build_style_directives( element_id, b.literal(directive.name), value, - /** @type {Expression} */ (directive.modifiers.includes('important') ? b.true : undefined), - force_check ? b.true : undefined + /** @type {Expression} */ (directive.modifiers.includes('important') ? b.true : undefined) ) ); diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index 91aa1aee65..b05dd8f9aa 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -105,6 +105,11 @@ 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; @@ -289,6 +294,10 @@ export function set_attributes( } } } + if (key === 'style' && '__styles' in element) { + // reset styles to force style: directive to update + element.__styles = {}; + } } // On the first run, ensure that events are added after bindings so diff --git a/packages/svelte/src/internal/client/dom/elements/style.js b/packages/svelte/src/internal/client/dom/elements/style.js index dd297ff72b..34531029c9 100644 --- a/packages/svelte/src/internal/client/dom/elements/style.js +++ b/packages/svelte/src/internal/client/dom/elements/style.js @@ -3,23 +3,20 @@ * @param {string} key * @param {string} value * @param {boolean} [important] - * @param {boolean} [force_check] Should be `true` if we can't rely on our cached value, because for example there's also a `style` attribute */ -export function set_style(dom, key, value, important, force_check) { +export function set_style(dom, key, value, important) { // @ts-expect-error - var attributes = (dom.__attributes ??= {}); - var style = dom.style; - var style_key = 'style-' + key; + var styles = (dom.__styles ??= {}); - if (attributes[style_key] === value && (!force_check || style.getPropertyValue(key) === value)) { + if (styles[key] === value) { return; } - attributes[style_key] = value; + styles[key] = value; if (value == null) { - style.removeProperty(key); + dom.style.removeProperty(key); } else { - style.setProperty(key, value, important ? 'important' : ''); + dom.style.setProperty(key, value, important ? 'important' : ''); } } diff --git a/packages/svelte/src/internal/client/dom/operations.js b/packages/svelte/src/internal/client/dom/operations.js index 76e5ca3cb2..e179ca0c71 100644 --- a/packages/svelte/src/internal/client/dom/operations.js +++ b/packages/svelte/src/internal/client/dom/operations.js @@ -44,6 +44,8 @@ export function init_operations() { // @ts-expect-error element_prototype.__attributes = null; // @ts-expect-error + element_prototype.__styles = null; + // @ts-expect-error element_prototype.__e = undefined; // @ts-expect-error From 041e56346615a146e452b69809f2c9d307a37909 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 24 Oct 2024 21:27:34 +0200 Subject: [PATCH 006/404] Version Packages (#13863) Co-authored-by: github-actions[bot] --- .changeset/little-hotels-poke.md | 5 ----- .changeset/nine-pigs-approve.md | 5 ----- .changeset/short-jokes-speak.md | 5 ----- .changeset/tasty-students-peel.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/little-hotels-poke.md delete mode 100644 .changeset/nine-pigs-approve.md delete mode 100644 .changeset/short-jokes-speak.md delete mode 100644 .changeset/tasty-students-peel.md diff --git a/.changeset/little-hotels-poke.md b/.changeset/little-hotels-poke.md deleted file mode 100644 index 50b60e2142..0000000000 --- a/.changeset/little-hotels-poke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: internally wrap store subscribe in untrack diff --git a/.changeset/nine-pigs-approve.md b/.changeset/nine-pigs-approve.md deleted file mode 100644 index b11daff5a3..0000000000 --- a/.changeset/nine-pigs-approve.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: allow binding to const with spread in legacy mode diff --git a/.changeset/short-jokes-speak.md b/.changeset/short-jokes-speak.md deleted file mode 100644 index 0b2552e29b..0000000000 --- a/.changeset/short-jokes-speak.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure props internally untracks current_value on sets diff --git a/.changeset/tasty-students-peel.md b/.changeset/tasty-students-peel.md deleted file mode 100644 index aa7c5472c5..0000000000 --- a/.changeset/tasty-students-peel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: properly traverse children when checking matches for `:has` diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 7fb83456f7..7aef0c6153 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,17 @@ # svelte +## 5.1.1 + +### Patch Changes + +- fix: internally wrap store subscribe in untrack ([#13858](https://github.com/sveltejs/svelte/pull/13858)) + +- fix: allow binding to const with spread in legacy mode ([#13849](https://github.com/sveltejs/svelte/pull/13849)) + +- fix: ensure props internally untracks current_value on sets ([#13859](https://github.com/sveltejs/svelte/pull/13859)) + +- fix: properly traverse children when checking matches for `:has` ([#13866](https://github.com/sveltejs/svelte/pull/13866)) + ## 5.1.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index c2689a69ab..07dce6fa5d 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.1.0", + "version": "5.1.1", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 357b807d80..164475d549 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.1.0'; +export const VERSION = '5.1.1'; export const PUBLIC_VERSION = '5'; From 5a54ad9cc723b401df7e8dc5084a3a5184f24316 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 24 Oct 2024 21:03:04 +0100 Subject: [PATCH 007/404] fix: ensure SvelteMap reactivity persists through deriveds (#13877) * fix: ensure SvelteMap reactivity persists through deriveds * fix: ensure SvelteMap reactivity persists through deriveds * fix: ensure SvelteMap reactivity persists through deriveds --- .changeset/hungry-dogs-happen.md | 5 +++ packages/svelte/src/reactivity/map.js | 11 +++++- .../samples/derived-map/_config.js | 13 +++++++ .../samples/derived-map/main.svelte | 34 +++++++++++++++++++ 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 .changeset/hungry-dogs-happen.md create mode 100644 packages/svelte/tests/runtime-runes/samples/derived-map/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/derived-map/main.svelte diff --git a/.changeset/hungry-dogs-happen.md b/.changeset/hungry-dogs-happen.md new file mode 100644 index 0000000000..e041a94c24 --- /dev/null +++ b/.changeset/hungry-dogs-happen.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure SvelteMap reactivity persists through deriveds diff --git a/packages/svelte/src/reactivity/map.js b/packages/svelte/src/reactivity/map.js index c732806cd0..ddadad90e2 100644 --- a/packages/svelte/src/reactivity/map.js +++ b/packages/svelte/src/reactivity/map.js @@ -94,13 +94,22 @@ export class SvelteMap extends Map { var s = sources.get(key); var prev_res = super.get(key); var res = super.set(key, value); + var version = this.#version; if (s === undefined) { sources.set(key, source(0)); set(this.#size, super.size); - increment(this.#version); + increment(version); } else if (prev_res !== value) { increment(s); + // If no one listening to this property and is listening to the version, or + // the inverse, then we should increment the version to be safe + if ( + (s.reactions === null && version.reactions !== null) || + (s.reactions !== null && version.reactions === null) + ) { + increment(version); + } } return res; diff --git a/packages/svelte/tests/runtime-runes/samples/derived-map/_config.js b/packages/svelte/tests/runtime-runes/samples/derived-map/_config.js new file mode 100644 index 0000000000..f3d115c62b --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/derived-map/_config.js @@ -0,0 +1,13 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + html: `Loading`, + + async test({ assert, target }) { + await Promise.resolve(); + flushSync(); + + assert.htmlEqual(target.innerHTML, `1`); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/derived-map/main.svelte b/packages/svelte/tests/runtime-runes/samples/derived-map/main.svelte new file mode 100644 index 0000000000..6278af8158 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/derived-map/main.svelte @@ -0,0 +1,34 @@ + + +{#if value instanceof Promise} + Loading +{:else} + {value} +{/if} + From 59ff7f1e88134a21024483377b502c6a94db6051 Mon Sep 17 00:00:00 2001 From: adiGuba Date: Thu, 24 Oct 2024 23:04:50 +0200 Subject: [PATCH 008/404] fix: date test fail for some timezone (#13889) * fix test for some timezone * lint --- packages/svelte/src/reactivity/date.test.ts | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/svelte/src/reactivity/date.test.ts b/packages/svelte/src/reactivity/date.test.ts index 77111ee727..d07b6780c6 100644 --- a/packages/svelte/src/reactivity/date.test.ts +++ b/packages/svelte/src/reactivity/date.test.ts @@ -38,15 +38,23 @@ test('date.setDate and date.setUTCDate', () => { date.setUTCDate(date.getUTCDate() + 1); }); + // Date/UTCDate may vary on some timezones + const date_plus_zero = new Date(initial_date); + date_plus_zero.setDate(a.getDate()); + const date_plus_one = new Date(initial_date); + date_plus_one.setDate(a.getDate() + 1); + const date_plus_two = new Date(initial_date); + date_plus_two.setDate(a.getDate() + 2); + assert.deepEqual(log, [ initial_date.getDate(), initial_date.getUTCDate(), - a.getDate(), - a.getUTCDate(), - a.getDate() + 1, - a.getUTCDate() + 1, - a.getDate() + 2, - a.getUTCDate() + 2 + date_plus_zero.getDate(), + date_plus_zero.getUTCDate(), + date_plus_one.getDate(), + date_plus_one.getUTCDate(), + date_plus_two.getDate(), + date_plus_two.getUTCDate() ]); cleanup(); From 4cf2d4a9049b1a32755ce3fad3418e81d6320ff3 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 24 Oct 2024 22:12:40 +0100 Subject: [PATCH 009/404] fix: ensure each block references to imports are handled correctly (#13892) --- .changeset/curvy-beds-cover.md | 5 +++++ packages/svelte/src/compiler/phases/2-analyze/index.js | 6 +++++- .../samples/each-block-ref-import/_config.js | 6 ++++++ .../samples/each-block-ref-import/main.svelte | 7 +++++++ .../samples/each-block-ref-import/utils.js | 9 +++++++++ 5 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 .changeset/curvy-beds-cover.md create mode 100644 packages/svelte/tests/runtime-legacy/samples/each-block-ref-import/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/each-block-ref-import/main.svelte create mode 100644 packages/svelte/tests/runtime-legacy/samples/each-block-ref-import/utils.js diff --git a/.changeset/curvy-beds-cover.md b/.changeset/curvy-beds-cover.md new file mode 100644 index 0000000000..f338cafffa --- /dev/null +++ b/.changeset/curvy-beds-cover.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure each block references to imports are handled correctly diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 188b060813..89c2e19c3f 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -505,7 +505,11 @@ export function analyze_component(root, source, options) { if (is_reference(node, parent)) { const binding = context.state.scope.get(node.name); - if (binding && binding.kind === 'normal') { + if ( + binding && + binding.kind === 'normal' && + binding.declaration_kind !== 'import' + ) { binding.kind = 'state'; binding.mutated = binding.updated = true; } diff --git a/packages/svelte/tests/runtime-legacy/samples/each-block-ref-import/_config.js b/packages/svelte/tests/runtime-legacy/samples/each-block-ref-import/_config.js new file mode 100644 index 0000000000..052cdd27c4 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/each-block-ref-import/_config.js @@ -0,0 +1,6 @@ +import { test } from '../../test'; + +export default test({ + html: ``, + ssrHtml: `` +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/each-block-ref-import/main.svelte b/packages/svelte/tests/runtime-legacy/samples/each-block-ref-import/main.svelte new file mode 100644 index 0000000000..26f629eb06 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/each-block-ref-import/main.svelte @@ -0,0 +1,7 @@ + + +{#each foo.bar as bar} + +{/each} \ No newline at end of file diff --git a/packages/svelte/tests/runtime-legacy/samples/each-block-ref-import/utils.js b/packages/svelte/tests/runtime-legacy/samples/each-block-ref-import/utils.js new file mode 100644 index 0000000000..e4b4434657 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/each-block-ref-import/utils.js @@ -0,0 +1,9 @@ +export const foo = { + get bar() { + return [ + { + value: '' + } + ]; + } +}; From 771b1e89055b0acd8134e1cdf36283d519dd3168 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 24 Oct 2024 22:12:48 +0100 Subject: [PATCH 010/404] fix: enable bound store props in runes mode components (#13887) * fix: enable bound store props in runes mode components * add some JSDoc, since it could be a headscratcher for future us * make it clear that this is specifically about bindings * skip intermediate value * tweak other names too --------- Co-authored-by: Rich Harris --- .changeset/cool-clocks-march.md | 5 +++ .../client/visitors/shared/component.js | 12 ++++++- packages/svelte/src/internal/client/index.js | 3 +- .../src/internal/client/reactivity/props.js | 11 +++++-- .../src/internal/client/reactivity/store.js | 33 +++++++++++++++++++ .../svelte/src/internal/client/validate.js | 6 +++- .../samples/bound-store-sub/Child.svelte | 7 ++++ .../samples/bound-store-sub/_config.js | 25 ++++++++++++++ .../samples/bound-store-sub/main.svelte | 11 +++++++ 9 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 .changeset/cool-clocks-march.md create mode 100644 packages/svelte/tests/runtime-runes/samples/bound-store-sub/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/bound-store-sub/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/bound-store-sub/main.svelte diff --git a/.changeset/cool-clocks-march.md b/.changeset/cool-clocks-march.md new file mode 100644 index 0000000000..b3d5705231 --- /dev/null +++ b/.changeset/cool-clocks-march.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: enable bound store props in runes mode components 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 e98c4f04f5..8e1a536707 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 @@ -188,7 +188,17 @@ export function build_component(node, component_name, context, anchor = context. ); } - push_prop(b.get(attribute.name, [b.return(expression)])); + const is_store_sub = + attribute.expression.type === 'Identifier' && + context.state.scope.get(attribute.expression.name)?.kind === 'store_sub'; + + if (is_store_sub) { + push_prop( + b.get(attribute.name, [b.stmt(b.call('$.mark_store_binding')), b.return(expression)]) + ); + } else { + push_prop(b.get(attribute.name, [b.return(expression)])); + } const assignment = b.assignment('=', attribute.expression, b.id('$$value')); push_prop( diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 306fc69ca7..c401867a0f 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -121,7 +121,8 @@ export { store_set, store_unsub, update_pre_store, - update_store + update_store, + mark_store_binding } from './reactivity/store.js'; export { set_text } from './render.js'; export { diff --git a/packages/svelte/src/internal/client/reactivity/props.js b/packages/svelte/src/internal/client/reactivity/props.js index 1df1933c19..eec8e70dc6 100644 --- a/packages/svelte/src/internal/client/reactivity/props.js +++ b/packages/svelte/src/internal/client/reactivity/props.js @@ -23,6 +23,7 @@ import { safe_equals } from './equality.js'; import * as e from '../errors.js'; import { BRANCH_EFFECT, DESTROYED, LEGACY_DERIVED_PROP, ROOT_EFFECT } from '../constants.js'; import { proxy } from '../proxy.js'; +import { capture_store_binding } from './store.js'; /** * @param {((value?: number) => number)} fn @@ -273,8 +274,14 @@ export function prop(props, key, flags, fallback) { var runes = (flags & PROPS_IS_RUNES) !== 0; var bindable = (flags & PROPS_IS_BINDABLE) !== 0; var lazy = (flags & PROPS_IS_LAZY_INITIAL) !== 0; + var is_store_sub = false; + var prop_value; - var prop_value = /** @type {V} */ (props[key]); + if (bindable) { + [prop_value, is_store_sub] = capture_store_binding(() => /** @type {V} */ (props[key])); + } else { + prop_value = /** @type {V} */ (props[key]); + } var setter = get_descriptor(props, key)?.set; var fallback_value = /** @type {V} */ (fallback); @@ -343,7 +350,7 @@ export function prop(props, key, flags, fallback) { // In that case the state proxy (if it exists) should take care of the notification. // If the parent is not in runes mode, we need to notify on mutation, too, that the prop // has changed because the parent will not be able to detect the change otherwise. - if (!runes || !mutation || legacy_parent) { + if (!runes || !mutation || legacy_parent || is_store_sub) { /** @type {Function} */ (setter)(mutation ? getter() : value); } return value; diff --git a/packages/svelte/src/internal/client/reactivity/store.js b/packages/svelte/src/internal/client/reactivity/store.js index e28202aaf0..11eee23e0a 100644 --- a/packages/svelte/src/internal/client/reactivity/store.js +++ b/packages/svelte/src/internal/client/reactivity/store.js @@ -6,6 +6,13 @@ import { get } from '../runtime.js'; import { teardown } from './effects.js'; import { mutable_source, set } from './sources.js'; +/** + * Whether or not the prop currently being read is a store binding, as in + * ``. If it is, we treat the prop as mutable even in + * runes mode, and skip `binding_property_non_reactive` validation + */ +let is_store_binding = false; + /** * 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 @@ -146,3 +153,29 @@ export function update_pre_store(store, store_value, d = 1) { store.set(value); return value; } + +/** + * Called inside prop getters to communicate that the prop is a store binding + */ +export function mark_store_binding() { + is_store_binding = true; +} + +/** + * Returns a tuple that indicates whether `fn()` reads a prop that is a store binding. + * Used to prevent `binding_property_non_reactive` validation false positives and + * ensure that these props are treated as mutable even in runes mode + * @template T + * @param {() => T} fn + * @returns {[T, boolean]} + */ +export function capture_store_binding(fn) { + var previous_is_store_binding = is_store_binding; + + try { + is_store_binding = false; + return [fn(), is_store_binding]; + } finally { + is_store_binding = previous_is_store_binding; + } +} diff --git a/packages/svelte/src/internal/client/validate.js b/packages/svelte/src/internal/client/validate.js index 0666e5f87b..c3b1821947 100644 --- a/packages/svelte/src/internal/client/validate.js +++ b/packages/svelte/src/internal/client/validate.js @@ -4,6 +4,7 @@ import * as e from './errors.js'; import { FILENAME } from '../../constants.js'; import { render_effect } from './reactivity/effects.js'; import * as w from './warnings.js'; +import { capture_store_binding } from './reactivity/store.js'; /** regex of all html void element names */ const void_element_names = @@ -84,7 +85,10 @@ export function validate_binding(binding, get_object, get_property, line, column render_effect(() => { if (warned) return; - var object = get_object(); + var [object, is_store_sub] = capture_store_binding(get_object); + + if (is_store_sub) return; + var property = get_property(); var ran = false; diff --git a/packages/svelte/tests/runtime-runes/samples/bound-store-sub/Child.svelte b/packages/svelte/tests/runtime-runes/samples/bound-store-sub/Child.svelte new file mode 100644 index 0000000000..8197a71d7c --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/bound-store-sub/Child.svelte @@ -0,0 +1,7 @@ + + +

+ +

diff --git a/packages/svelte/tests/runtime-runes/samples/bound-store-sub/_config.js b/packages/svelte/tests/runtime-runes/samples/bound-store-sub/_config.js new file mode 100644 index 0000000000..a2ba61718c --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/bound-store-sub/_config.js @@ -0,0 +1,25 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; +import { ok } from 'assert'; + +export default test({ + compileOptions: { + dev: true + }, + + html: `

\n{"count":0}`, + ssrHtml: `

\n{"count":0}`, + + test({ assert, target }) { + const input = target.querySelector('input'); + ok(input); + const inputEvent = new window.InputEvent('input'); + + input.value = '10'; + input.dispatchEvent(inputEvent); + + flushSync(); + + assert.htmlEqual(target.innerHTML, `

\n{"count":10}`); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/bound-store-sub/main.svelte b/packages/svelte/tests/runtime-runes/samples/bound-store-sub/main.svelte new file mode 100644 index 0000000000..6ecc4f156e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/bound-store-sub/main.svelte @@ -0,0 +1,11 @@ + + + +{JSON.stringify($form)} From b5750ac6678e18e67527db6877d7474f0e5fa1fe Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Thu, 24 Oct 2024 23:18:15 +0200 Subject: [PATCH 011/404] chore: setup `pkg.pr.new` (#13891) * chore: setup `pkg.pr.new` * chore: let's try with this --- .github/workflows/pkg.pr.new.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/pkg.pr.new.yml diff --git a/.github/workflows/pkg.pr.new.yml b/.github/workflows/pkg.pr.new.yml new file mode 100644 index 0000000000..76736782a0 --- /dev/null +++ b/.github/workflows/pkg.pr.new.yml @@ -0,0 +1,24 @@ +name: Publish Any Commit +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - run: corepack enable + - uses: actions/setup-node@v4 + with: + node-version: 18.x + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build + run: pnpm build + + - run: pnpx pkg-pr-new publish --compact --no-template './packages/svelte' From 42ccdc1023f2169fda6aa4e4419238f9b31fe387 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 24 Oct 2024 22:21:01 +0100 Subject: [PATCH 012/404] fix: ensure snippets after empty text correctly hydrate (#13870) --- .changeset/plenty-plums-sparkle.md | 5 +++++ .../phases/3-transform/client/visitors/RegularElement.js | 2 +- packages/svelte/src/internal/client/dom/operations.js | 8 +++++++- .../runtime-runes/samples/snippet-after-text/Child.svelte | 5 +++++ .../runtime-runes/samples/snippet-after-text/_config.js | 8 ++++++++ .../runtime-runes/samples/snippet-after-text/main.svelte | 5 +++++ .../skip-static-subtree/_expected/client/index.svelte.js | 2 +- 7 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 .changeset/plenty-plums-sparkle.md create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-after-text/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-after-text/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-after-text/main.svelte diff --git a/.changeset/plenty-plums-sparkle.md b/.changeset/plenty-plums-sparkle.md new file mode 100644 index 0000000000..4613cc321e --- /dev/null +++ b/.changeset/plenty-plums-sparkle.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure snippets after empty text correctly hydrate 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 d748fd85e8..52100a2019 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 @@ -384,7 +384,7 @@ export function RegularElement(node, context) { arg = b.member(arg, 'content'); } - process_children(trimmed, () => b.call('$.child', arg), true, { + process_children(trimmed, (is_text) => b.call('$.child', arg, is_text && b.true), true, { ...context, state: child_state }); diff --git a/packages/svelte/src/internal/client/dom/operations.js b/packages/svelte/src/internal/client/dom/operations.js index e179ca0c71..6dfce9ebd3 100644 --- a/packages/svelte/src/internal/client/dom/operations.js +++ b/packages/svelte/src/internal/client/dom/operations.js @@ -91,9 +91,10 @@ export function get_next_sibling(node) { * Don't mark this as side-effect-free, hydration needs to walk all nodes * @template {Node} N * @param {N} node + * @param {boolean} is_text * @returns {Node | null} */ -export function child(node) { +export function child(node, is_text) { if (!hydrating) { return get_first_child(node); } @@ -103,6 +104,11 @@ export function child(node) { // Child can be null if we have an element with a single child, like `

{text}

`, where `text` is empty if (child === null) { child = hydrate_node.appendChild(create_text()); + } else if (is_text && child.nodeType !== 3) { + var text = create_text(); + child?.before(text); + set_hydrate_node(text); + return text; } set_hydrate_node(child); diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-after-text/Child.svelte b/packages/svelte/tests/runtime-runes/samples/snippet-after-text/Child.svelte new file mode 100644 index 0000000000..711171a21d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-after-text/Child.svelte @@ -0,0 +1,5 @@ + + +
{prop}{@render children()}
\ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-after-text/_config.js b/packages/svelte/tests/runtime-runes/samples/snippet-after-text/_config.js new file mode 100644 index 0000000000..d9aa3a46c2 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-after-text/_config.js @@ -0,0 +1,8 @@ +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true // Render in dev mode to check that the validation error is not thrown + }, + html: `
123 + import Child from './Child.svelte'; + + +123 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 76433ad5a7..2d3cc3ba5a 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 @@ -7,7 +7,7 @@ export default function Skip_static_subtree($$anchor, $$props) { var fragment = root(); var main = $.sibling($.first_child(fragment), 2); var h1 = $.child(main); - var text = $.child(h1); + var text = $.child(h1, true); $.reset(h1); From d0177711b110c6d1b6b598c91063cc31841534f4 Mon Sep 17 00:00:00 2001 From: adiGuba Date: Thu, 24 Oct 2024 23:24:29 +0200 Subject: [PATCH 013/404] fix : Rapidly toggling element with out: transition causes incorrect node removal (#13884) * set onfinish to null to fix a rare bug * replace null by noop --- .../svelte/src/internal/client/dom/elements/transitions.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/svelte/src/internal/client/dom/elements/transitions.js b/packages/svelte/src/internal/client/dom/elements/transitions.js index c8c297a394..dee029e88f 100644 --- a/packages/svelte/src/internal/client/dom/elements/transitions.js +++ b/packages/svelte/src/internal/client/dom/elements/transitions.js @@ -427,6 +427,10 @@ function animate(element, options, counterpart, t2, on_finish) { animation.cancel(); // This prevents memory leaks in Chromium animation.effect = null; + // This prevents onfinish to be launched after cancel(), + // which can happen in some rare cases + // see https://github.com/sveltejs/svelte/issues/13681 + animation.onfinish = noop; } }, deactivate: () => { From dee8db53df03848466ec45aea240c30a750e4a9f Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 24 Oct 2024 22:32:54 +0100 Subject: [PATCH 014/404] chore: add missing changeset (#13895) Adds a changeset for #13884 --- .changeset/cold-owls-do.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/cold-owls-do.md diff --git a/.changeset/cold-owls-do.md b/.changeset/cold-owls-do.md new file mode 100644 index 0000000000..46f14c6ea9 --- /dev/null +++ b/.changeset/cold-owls-do.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: improve consistency of transitions From 31472878130a5b508f4875a6423f5e8a0709e3c9 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Fri, 25 Oct 2024 00:04:34 +0200 Subject: [PATCH 015/404] fix: prevent var name clashing for delegated events without params (#13896) --- .changeset/smooth-dragons-push.md | 5 +++++ .../compiler/phases/3-transform/client/utils.js | 2 +- .../samples/event-without-params-clash/_config.js | 14 ++++++++++++++ .../samples/event-without-params-clash/main.svelte | 6 ++++++ 4 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 .changeset/smooth-dragons-push.md create mode 100644 packages/svelte/tests/runtime-runes/samples/event-without-params-clash/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/event-without-params-clash/main.svelte diff --git a/.changeset/smooth-dragons-push.md b/.changeset/smooth-dragons-push.md new file mode 100644 index 0000000000..6e02748b47 --- /dev/null +++ b/.changeset/smooth-dragons-push.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: prevent var name clashing for delegated events without params 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 9463d909bc..2647f0a3be 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/utils.js @@ -136,7 +136,7 @@ export function build_hoisted_params(node, context) { if (node.params.length === 0) { if (hoisted_params.length > 0) { // For the event object - params.push(b.id('_')); + params.push(b.id(context.state.scope.generate('_'))); } } else { for (const param of node.params) { diff --git a/packages/svelte/tests/runtime-runes/samples/event-without-params-clash/_config.js b/packages/svelte/tests/runtime-runes/samples/event-without-params-clash/_config.js new file mode 100644 index 0000000000..3dc265319d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-without-params-clash/_config.js @@ -0,0 +1,14 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + mode: ['client'], + test({ assert, target, logs }) { + const btn = target.querySelector('button'); + + flushSync(() => { + btn?.click(); + }); + assert.deepEqual(logs, ['test']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/event-without-params-clash/main.svelte b/packages/svelte/tests/runtime-runes/samples/event-without-params-clash/main.svelte new file mode 100644 index 0000000000..eea08b3473 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-without-params-clash/main.svelte @@ -0,0 +1,6 @@ + + + + \ No newline at end of file From b63fde3b75a77fb9fec3a9e126a8f0643ebb5d28 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Fri, 25 Oct 2024 00:54:22 +0200 Subject: [PATCH 016/404] fix: prevent migration script from adding `props.` to the `export let` identifier (#13899) --- .changeset/shaggy-seals-judge.md | 5 +++++ packages/svelte/src/compiler/migrate/index.js | 2 +- .../samples/not-prepend-props-to-export-let/input.svelte | 5 +++++ .../samples/not-prepend-props-to-export-let/output.svelte | 5 +++++ 4 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 .changeset/shaggy-seals-judge.md create mode 100644 packages/svelte/tests/migrate/samples/not-prepend-props-to-export-let/input.svelte create mode 100644 packages/svelte/tests/migrate/samples/not-prepend-props-to-export-let/output.svelte diff --git a/.changeset/shaggy-seals-judge.md b/.changeset/shaggy-seals-judge.md new file mode 100644 index 0000000000..ef79705c31 --- /dev/null +++ b/.changeset/shaggy-seals-judge.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: prevent migration script from adding `props.` to the `export let` identifier diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index 057020d730..ceb2ad5da2 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -1514,7 +1514,7 @@ function handle_identifier(node, state, path) { ); } else { const binding = state.scope.get(node.name); - if (binding?.kind === 'bindable_prop') { + if (binding?.kind === 'bindable_prop' && binding.node !== node) { state.str.prependLeft(/** @type {number} */ (node.start), `${state.names.props}.`); } } diff --git a/packages/svelte/tests/migrate/samples/not-prepend-props-to-export-let/input.svelte b/packages/svelte/tests/migrate/samples/not-prepend-props-to-export-let/input.svelte new file mode 100644 index 0000000000..b169f63045 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/not-prepend-props-to-export-let/input.svelte @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/not-prepend-props-to-export-let/output.svelte b/packages/svelte/tests/migrate/samples/not-prepend-props-to-export-let/output.svelte new file mode 100644 index 0000000000..8e0f2fd658 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/not-prepend-props-to-export-let/output.svelte @@ -0,0 +1,5 @@ + \ No newline at end of file From 1ee2ac4d05ae70d61d2fdc5bfaaddbee47e5c6c6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 25 Oct 2024 00:30:46 +0100 Subject: [PATCH 017/404] Version Packages (#13888) Co-authored-by: github-actions[bot] --- .changeset/cold-owls-do.md | 5 ----- .changeset/cool-clocks-march.md | 5 ----- .changeset/curvy-beds-cover.md | 5 ----- .changeset/hungry-dogs-happen.md | 5 ----- .changeset/plenty-plums-sparkle.md | 5 ----- .changeset/shaggy-seals-judge.md | 5 ----- .changeset/smooth-dragons-push.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/cold-owls-do.md delete mode 100644 .changeset/cool-clocks-march.md delete mode 100644 .changeset/curvy-beds-cover.md delete mode 100644 .changeset/hungry-dogs-happen.md delete mode 100644 .changeset/plenty-plums-sparkle.md delete mode 100644 .changeset/shaggy-seals-judge.md delete mode 100644 .changeset/smooth-dragons-push.md diff --git a/.changeset/cold-owls-do.md b/.changeset/cold-owls-do.md deleted file mode 100644 index 46f14c6ea9..0000000000 --- a/.changeset/cold-owls-do.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: improve consistency of transitions diff --git a/.changeset/cool-clocks-march.md b/.changeset/cool-clocks-march.md deleted file mode 100644 index b3d5705231..0000000000 --- a/.changeset/cool-clocks-march.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: enable bound store props in runes mode components diff --git a/.changeset/curvy-beds-cover.md b/.changeset/curvy-beds-cover.md deleted file mode 100644 index f338cafffa..0000000000 --- a/.changeset/curvy-beds-cover.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure each block references to imports are handled correctly diff --git a/.changeset/hungry-dogs-happen.md b/.changeset/hungry-dogs-happen.md deleted file mode 100644 index e041a94c24..0000000000 --- a/.changeset/hungry-dogs-happen.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure SvelteMap reactivity persists through deriveds diff --git a/.changeset/plenty-plums-sparkle.md b/.changeset/plenty-plums-sparkle.md deleted file mode 100644 index 4613cc321e..0000000000 --- a/.changeset/plenty-plums-sparkle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure snippets after empty text correctly hydrate diff --git a/.changeset/shaggy-seals-judge.md b/.changeset/shaggy-seals-judge.md deleted file mode 100644 index ef79705c31..0000000000 --- a/.changeset/shaggy-seals-judge.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: prevent migration script from adding `props.` to the `export let` identifier diff --git a/.changeset/smooth-dragons-push.md b/.changeset/smooth-dragons-push.md deleted file mode 100644 index 6e02748b47..0000000000 --- a/.changeset/smooth-dragons-push.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: prevent var name clashing for delegated events without params diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 7aef0c6153..e84220e4e2 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,23 @@ # svelte +## 5.1.2 + +### Patch Changes + +- fix: improve consistency of transitions ([#13895](https://github.com/sveltejs/svelte/pull/13895)) + +- fix: enable bound store props in runes mode components ([#13887](https://github.com/sveltejs/svelte/pull/13887)) + +- fix: ensure each block references to imports are handled correctly ([#13892](https://github.com/sveltejs/svelte/pull/13892)) + +- fix: ensure SvelteMap reactivity persists through deriveds ([#13877](https://github.com/sveltejs/svelte/pull/13877)) + +- fix: ensure snippets after empty text correctly hydrate ([#13870](https://github.com/sveltejs/svelte/pull/13870)) + +- fix: prevent migration script from adding `props.` to the `export let` identifier ([#13899](https://github.com/sveltejs/svelte/pull/13899)) + +- fix: prevent var name clashing for delegated events without params ([#13896](https://github.com/sveltejs/svelte/pull/13896)) + ## 5.1.1 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 07dce6fa5d..c01ca6429b 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.1.1", + "version": "5.1.2", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 164475d549..434d9199f5 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.1.1'; +export const VERSION = '5.1.2'; export const PUBLIC_VERSION = '5'; From 18e2284e749a6a4334d56e1bb40e3ef4fc140bb0 Mon Sep 17 00:00:00 2001 From: plgingras88 <34991510+plgingras88@users.noreply.github.com> Date: Thu, 24 Oct 2024 21:56:44 -0400 Subject: [PATCH 018/404] docs: fix setContext/getContext typo (#13901) --- documentation/docs/06-runtime/02-context.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/docs/06-runtime/02-context.md b/documentation/docs/06-runtime/02-context.md index 87f254129e..62dd0c6a9e 100644 --- a/documentation/docs/06-runtime/02-context.md +++ b/documentation/docs/06-runtime/02-context.md @@ -80,9 +80,9 @@ Context is not inherently reactive. If you need reactive values in context then ```svelte

Count is {value.count}

From 1a6a3e7ebd27b55f4ad00197fddc7e197399bb1a Mon Sep 17 00:00:00 2001 From: Matthew Carroll <53098729+matthewCmatt@users.noreply.github.com> Date: Fri, 25 Oct 2024 03:41:54 -0400 Subject: [PATCH 019/404] fix: typo in `Action` types (#13874) * typo: HTMLDiveElement * regenerate types * add changeset --------- Co-authored-by: Conduitry --- .changeset/strong-buttons-juggle.md | 5 +++++ packages/svelte/src/action/public.d.ts | 2 +- packages/svelte/types/index.d.ts | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/strong-buttons-juggle.md diff --git a/.changeset/strong-buttons-juggle.md b/.changeset/strong-buttons-juggle.md new file mode 100644 index 0000000000..4a605f8051 --- /dev/null +++ b/.changeset/strong-buttons-juggle.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: typo in `Action` types diff --git a/packages/svelte/src/action/public.d.ts b/packages/svelte/src/action/public.d.ts index 5d23b4d608..94669ff1fd 100644 --- a/packages/svelte/src/action/public.d.ts +++ b/packages/svelte/src/action/public.d.ts @@ -48,7 +48,7 @@ export interface ActionReturn< * // ... * } * ``` - * `Action` and `Action` both signal that the action accepts no parameters. + * `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. diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 49ff2db8ce..4e57607efc 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -546,7 +546,7 @@ declare module 'svelte/action' { * // ... * } * ``` - * `Action` and `Action` both signal that the action accepts no parameters. + * `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. From 107be2889009f15ce9d0ff15bd94244e00584cd9 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Fri, 25 Oct 2024 04:49:08 -0400 Subject: [PATCH 020/404] docs: fix outdated $effect.tracking() demo (#13906) --- 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 eedca52d9d..07291daa43 100644 --- a/documentation/docs/02-runes/04-$effect.md +++ b/documentation/docs/02-runes/04-$effect.md @@ -179,7 +179,7 @@ Apart from the timing, `$effect.pre` works exactly like `$effect`. ## `$effect.tracking` -The `$effect.tracking` rune is an advanced feature that tells you whether or not the code is running inside a tracking context, such as an effect or inside your template ([demo](/playground/untitled#H4sIAAAAAAAAE3XP0QrCMAwF0F-JRXAD595rLfgdzodRUyl0bVgzQcb-3VYFQfExl5tDMgvrPCYhT7MI_YBCiiOR2Aq-UxnSDT1jnlOcRlMSlczoiHUXOjYxpOhx5-O12rgAJg4UAwaGhDyR3Gxhjdai4V1v2N2wqus9tC3Y3ifMQjbehaqq4aBhLtEv_Or893icCsdLve-Caj8nBkU67zMO5HtGCfM3sKiWNKhV0zwVaBqd3x3ixVmHFyFLuJyXB-moOe8pAQAA)): +The `$effect.tracking` rune is an advanced feature that tells you whether or not the code is running inside a tracking context, such as an effect or inside your template ([demo](/playground/untitled#H4sIAAAAAAAACn3PwYrCMBDG8VeZDYIt2PYeY8Dn2HrIhqkU08nQjItS-u6buAt7UDzmz8ePyaKGMWBS-nNRcmdU-hHUTpGbyuvI3KZvDFLal0v4qvtIgiSZUSb5eWSxPfWSc4oB2xDP1XYk8HHiSHkICeXKeruDDQ4Demlldv4y0rmq6z10HQwuJMxGVv4mVVXDwcJS0jP9u3knynwtoKz1vifT_Z9Jhm0WBCcOTlDD8kyspmML5qNpHg40jc3fFryJ0iWsp_UHgz3180oBAAA=)): ```svelte + + + +{#if toggle} + {#await promise}{/await} +{/if} From 1e6cf1bb408acb787ebab001b4301b961b76bbed Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Fri, 25 Oct 2024 22:02:45 +0100 Subject: [PATCH 025/404] fix: ensure bind:group works as intended with proxied state objects (#13939) --- .changeset/giant-bags-wash.md | 5 +++++ .../client/dom/elements/bindings/input.js | 4 ++-- .../samples/state-bind-group/_config.js | 19 +++++++++++++++++++ .../samples/state-bind-group/main.svelte | 12 ++++++++++++ 4 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 .changeset/giant-bags-wash.md create mode 100644 packages/svelte/tests/runtime-runes/samples/state-bind-group/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/state-bind-group/main.svelte diff --git a/.changeset/giant-bags-wash.md b/.changeset/giant-bags-wash.md new file mode 100644 index 0000000000..5731e4f922 --- /dev/null +++ b/.changeset/giant-bags-wash.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure bind:group works as intended with proxied state objects 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 28a99f7eed..7af20b932a 100644 --- a/packages/svelte/src/internal/client/dom/elements/bindings/input.js +++ b/packages/svelte/src/internal/client/dom/elements/bindings/input.js @@ -2,7 +2,7 @@ import { DEV } from 'esm-env'; import { render_effect, teardown } from '../../../reactivity/effects.js'; import { listen_to_event_and_reset_event } from './shared.js'; import * as e from '../../../errors.js'; -import { get_proxied_value, is } from '../../../proxy.js'; +import { is } from '../../../proxy.js'; import { queue_micro_task } from '../../task.js'; import { hydrating } from '../../hydration.js'; import { is_runes } from '../../../runtime.js'; @@ -126,7 +126,7 @@ export function bind_group(inputs, group_index, input, get, set = get) { if (is_checkbox) { value = value || []; // @ts-ignore - input.checked = get_proxied_value(value).includes(get_proxied_value(input.__value)); + input.checked = value.includes(input.__value); } else { // @ts-ignore input.checked = is(input.__value, value); diff --git a/packages/svelte/tests/runtime-runes/samples/state-bind-group/_config.js b/packages/svelte/tests/runtime-runes/samples/state-bind-group/_config.js new file mode 100644 index 0000000000..72f49c6e0e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/state-bind-group/_config.js @@ -0,0 +1,19 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + html: `\n[]`, + + test({ assert, target }) { + const btn = target.querySelector('button'); + + btn?.click(); + flushSync(); + + assert.equal(target.querySelectorAll('input')[1].checked, true); + assert.htmlEqual( + target.innerHTML, + `\n["2"]` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/state-bind-group/main.svelte b/packages/svelte/tests/runtime-runes/samples/state-bind-group/main.svelte new file mode 100644 index 0000000000..ca78457ee8 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/state-bind-group/main.svelte @@ -0,0 +1,12 @@ + + + + +{#each values as val, i} + +{/each} + +{JSON.stringify(checkboxes)} From 37fa34c4ade50b6570030669cdccc6c6c24226b5 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Fri, 25 Oct 2024 22:04:36 +0100 Subject: [PATCH 026/404] fix: ensure value is correctly set to zero on the progress element (#13924) * fix: ensure value is correctly set to zero on the progress element * fix: ensure value is correctly set to zero on the progress element * fix: ensure value is correctly set to zero on the progress element --- .changeset/late-wombats-cheer.md | 5 +++++ .../src/internal/client/dom/elements/attributes.js | 9 +++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 .changeset/late-wombats-cheer.md diff --git a/.changeset/late-wombats-cheer.md b/.changeset/late-wombats-cheer.md new file mode 100644 index 0000000000..cf3f8a0d2b --- /dev/null +++ b/.changeset/late-wombats-cheer.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure value is correctly set to zero on the progress element diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index b05dd8f9aa..30c7712f1e 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -54,8 +54,13 @@ export function remove_input_defaults(input) { export function set_value(element, value) { // @ts-expect-error var attributes = (element.__attributes ??= {}); - // @ts-expect-error - if (attributes.value === (attributes.value = value) || element.value === value) return; + if ( + attributes.value === (attributes.value = value) || + // @ts-expect-error + // `progress` elements always need their value set when its `0` + (element.value === value && (value !== 0 || element.nodeName !== 'PROGRESS')) + ) + return; // @ts-expect-error element.value = value; } From 83a5eaac8e3ad2c549c640549d84b6bd3efd01c4 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Fri, 25 Oct 2024 22:04:54 +0100 Subject: [PATCH 027/404] fix: skip comment nodes in snippet validation logic (#13936) --- .changeset/shy-penguins-beg.md | 5 +++++ .../compiler/phases/2-analyze/visitors/SnippetBlock.js | 5 ++++- .../samples/snippet-comment-sibling/Child.svelte | 5 +++++ .../samples/snippet-comment-sibling/_config.js | 5 +++++ .../samples/snippet-comment-sibling/main.svelte | 10 ++++++++++ 5 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 .changeset/shy-penguins-beg.md create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-comment-sibling/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-comment-sibling/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-comment-sibling/main.svelte diff --git a/.changeset/shy-penguins-beg.md b/.changeset/shy-penguins-beg.md new file mode 100644 index 0000000000..77b614c3d5 --- /dev/null +++ b/.changeset/shy-penguins-beg.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: skip comment nodes in snippet validation logic diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/SnippetBlock.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/SnippetBlock.js index 94bbd3cde1..356cf2eeae 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/SnippetBlock.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/SnippetBlock.js @@ -46,7 +46,10 @@ export function SnippetBlock(node, context) { ) { if ( parent.fragment.nodes.some( - (node) => node.type !== 'SnippetBlock' && (node.type !== 'Text' || node.data.trim()) + (node) => + node.type !== 'SnippetBlock' && + (node.type !== 'Text' || node.data.trim()) && + node.type !== 'Comment' ) ) { e.snippet_conflict(node); diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-comment-sibling/Child.svelte b/packages/svelte/tests/runtime-runes/samples/snippet-comment-sibling/Child.svelte new file mode 100644 index 0000000000..5da1d878e8 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-comment-sibling/Child.svelte @@ -0,0 +1,5 @@ + + +{@render children()} \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-comment-sibling/_config.js b/packages/svelte/tests/runtime-runes/samples/snippet-comment-sibling/_config.js new file mode 100644 index 0000000000..17eaeb9553 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-comment-sibling/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + html: `The content` +}); diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-comment-sibling/main.svelte b/packages/svelte/tests/runtime-runes/samples/snippet-comment-sibling/main.svelte new file mode 100644 index 0000000000..c1073ede06 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-comment-sibling/main.svelte @@ -0,0 +1,10 @@ + + + + + {#snippet children()} + The content + {/snippet} + From da1fefcd45c7bc8650db78ab35e08a921747761c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 25 Oct 2024 22:13:06 +0100 Subject: [PATCH 028/404] Version Packages (#13920) Co-authored-by: github-actions[bot] --- .changeset/blue-boats-call.md | 5 ----- .changeset/gentle-bulldogs-cough.md | 5 ----- .changeset/giant-bags-wash.md | 5 ----- .changeset/late-wombats-cheer.md | 5 ----- .changeset/shy-penguins-beg.md | 5 ----- .changeset/strong-buttons-juggle.md | 5 ----- .changeset/strong-dogs-obey.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/blue-boats-call.md delete mode 100644 .changeset/gentle-bulldogs-cough.md delete mode 100644 .changeset/giant-bags-wash.md delete mode 100644 .changeset/late-wombats-cheer.md delete mode 100644 .changeset/shy-penguins-beg.md delete mode 100644 .changeset/strong-buttons-juggle.md delete mode 100644 .changeset/strong-dogs-obey.md diff --git a/.changeset/blue-boats-call.md b/.changeset/blue-boats-call.md deleted file mode 100644 index 0f6c31025d..0000000000 --- a/.changeset/blue-boats-call.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: rethrow errors from await block if no catch block exists diff --git a/.changeset/gentle-bulldogs-cough.md b/.changeset/gentle-bulldogs-cough.md deleted file mode 100644 index 3f6f862259..0000000000 --- a/.changeset/gentle-bulldogs-cough.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure SVG element attributes have case preserved diff --git a/.changeset/giant-bags-wash.md b/.changeset/giant-bags-wash.md deleted file mode 100644 index 5731e4f922..0000000000 --- a/.changeset/giant-bags-wash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure bind:group works as intended with proxied state objects diff --git a/.changeset/late-wombats-cheer.md b/.changeset/late-wombats-cheer.md deleted file mode 100644 index cf3f8a0d2b..0000000000 --- a/.changeset/late-wombats-cheer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure value is correctly set to zero on the progress element diff --git a/.changeset/shy-penguins-beg.md b/.changeset/shy-penguins-beg.md deleted file mode 100644 index 77b614c3d5..0000000000 --- a/.changeset/shy-penguins-beg.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: skip comment nodes in snippet validation logic diff --git a/.changeset/strong-buttons-juggle.md b/.changeset/strong-buttons-juggle.md deleted file mode 100644 index 4a605f8051..0000000000 --- a/.changeset/strong-buttons-juggle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: typo in `Action` types diff --git a/.changeset/strong-dogs-obey.md b/.changeset/strong-dogs-obey.md deleted file mode 100644 index abab6fdf75..0000000000 --- a/.changeset/strong-dogs-obey.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: remove metadata from legacy AST diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index e84220e4e2..ae56d2e619 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,23 @@ # svelte +## 5.1.3 + +### Patch Changes + +- fix: rethrow errors from await block if no catch block exists ([#13819](https://github.com/sveltejs/svelte/pull/13819)) + +- fix: ensure SVG element attributes have case preserved ([#13935](https://github.com/sveltejs/svelte/pull/13935)) + +- fix: ensure bind:group works as intended with proxied state objects ([#13939](https://github.com/sveltejs/svelte/pull/13939)) + +- fix: ensure value is correctly set to zero on the progress element ([#13924](https://github.com/sveltejs/svelte/pull/13924)) + +- fix: skip comment nodes in snippet validation logic ([#13936](https://github.com/sveltejs/svelte/pull/13936)) + +- fix: typo in `Action` types ([#13874](https://github.com/sveltejs/svelte/pull/13874)) + +- fix: remove metadata from legacy AST ([#13927](https://github.com/sveltejs/svelte/pull/13927)) + ## 5.1.2 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index c01ca6429b..73b3e253f3 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.1.2", + "version": "5.1.3", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 434d9199f5..1e37585b9d 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.1.2'; +export const VERSION = '5.1.3'; export const PUBLIC_VERSION = '5'; From 7be3afb3a642d1654a4b96ddfa3d6239a8cf1a6b Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Sat, 26 Oct 2024 09:20:06 +0200 Subject: [PATCH 029/404] fix: add empty stack to `CompileDiagnostic` to show error on build (#13942) * fix: add empty stack to `CompileDiagnostic` to show error on build * chore: generate types * chore: make stack optional to make TS happy * chore: generate types -.-" * chore: add comment --- .changeset/brave-fans-sneeze.md | 5 +++++ packages/svelte/src/compiler/utils/compile_diagnostic.js | 3 +++ .../samples/impossible-migrate-with-errors/_config.js | 1 + packages/svelte/types/index.d.ts | 2 ++ 4 files changed, 11 insertions(+) create mode 100644 .changeset/brave-fans-sneeze.md diff --git a/.changeset/brave-fans-sneeze.md b/.changeset/brave-fans-sneeze.md new file mode 100644 index 0000000000..c5fe894acb --- /dev/null +++ b/.changeset/brave-fans-sneeze.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: add empty stack to `CompileDiagnostic` to show error on build diff --git a/packages/svelte/src/compiler/utils/compile_diagnostic.js b/packages/svelte/src/compiler/utils/compile_diagnostic.js index a27e06968f..374a20b8b5 100644 --- a/packages/svelte/src/compiler/utils/compile_diagnostic.js +++ b/packages/svelte/src/compiler/utils/compile_diagnostic.js @@ -39,6 +39,7 @@ function get_code_frame(source, line, column) { * @typedef {{ * code: string; * message: string; + * stack?: string; * filename?: string; * start?: Location; * end?: Location; @@ -50,6 +51,8 @@ function get_code_frame(source, line, column) { /** @implements {ICompileDiagnostic} */ export class CompileDiagnostic { name = 'CompileDiagnostic'; + // adding an empty stack so that vite will show the file and frame during build + stack = ''; /** * @param {string} code diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-with-errors/_config.js b/packages/svelte/tests/migrate/samples/impossible-migrate-with-errors/_config.js index 6d25bb63b1..57496dcba7 100644 --- a/packages/svelte/tests/migrate/samples/impossible-migrate-with-errors/_config.js +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-with-errors/_config.js @@ -20,6 +20,7 @@ export default test({ ^`, message: 'Unexpected end of input', name: 'CompileError', + stack: '', position: [30, 30], start: { character: 30, diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 4e57607efc..c7794d5b0e 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -1458,6 +1458,7 @@ declare module 'svelte/compiler' { type ICompileDiagnostic = { code: string; message: string; + stack?: string; filename?: string; start?: Location; end?: Location; @@ -2286,6 +2287,7 @@ declare module 'svelte/types/compiler/interfaces' { type ICompileDiagnostic = { code: string; message: string; + stack?: string; filename?: string; start?: Location; end?: Location; From 12fcc7a35a9fedfa5cf3cb85b4cc904b75f2aff5 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Sat, 26 Oct 2024 18:20:37 +0200 Subject: [PATCH 030/404] fix: trim whitespace while migrating blocks (#13941) Co-authored-by: Oscar Dominguez --- .changeset/tasty-frogs-boil.md | 5 ++ packages/svelte/src/compiler/migrate/index.js | 54 +++++++++++++++++++ .../remove-blocks-whitespace/input.svelte | 27 ++++++++++ .../remove-blocks-whitespace/output.svelte | 26 +++++++++ 4 files changed, 112 insertions(+) create mode 100644 .changeset/tasty-frogs-boil.md create mode 100644 packages/svelte/tests/migrate/samples/remove-blocks-whitespace/input.svelte create mode 100644 packages/svelte/tests/migrate/samples/remove-blocks-whitespace/output.svelte diff --git a/.changeset/tasty-frogs-boil.md b/.changeset/tasty-frogs-boil.md new file mode 100644 index 0000000000..26b33db998 --- /dev/null +++ b/.changeset/tasty-frogs-boil.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: trim whitespace while migrating blocks diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index ceb2ad5da2..0bf3ac08b2 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -853,6 +853,20 @@ const instance_script = { } }; +/** + * + * @param {State} state + * @param {number} start + * @param {number} end + */ +function trim_block(state, start, end) { + const original = state.str.snip(start, end).toString(); + const without_parens = original.substring(1, original.length - 1); + if (without_parens.trim().length !== without_parens.length) { + state.str.update(start + 1, end - 1, without_parens.trim()); + } +} + /** @type {Visitors} */ const template = { Identifier(node, { state, path }) { @@ -1119,6 +1133,46 @@ const template = { if (migrated !== node.data) { state.str.overwrite(node.start + ''.length, migrated); } + }, + HtmlTag(node, { state, next }) { + trim_block(state, node.start, node.end); + next(); + }, + ConstTag(node, { state, next }) { + trim_block(state, node.start, node.end); + next(); + }, + IfBlock(node, { state, next }) { + const start = node.start; + const end = state.str.original.indexOf('}', node.test.end) + 1; + trim_block(state, start, end); + next(); + }, + AwaitBlock(node, { state, next }) { + const start = node.start; + const end = + state.str.original.indexOf( + '}', + node.pending !== null ? node.expression.end : node.value?.end + ) + 1; + trim_block(state, start, end); + if (node.pending !== null) { + const start = state.str.original.lastIndexOf('{', node.value?.start); + const end = state.str.original.indexOf('}', node.value?.end) + 1; + trim_block(state, start, end); + } + if (node.catch !== null) { + const start = state.str.original.lastIndexOf('{', node.error?.start); + const end = state.str.original.indexOf('}', node.error?.end) + 1; + trim_block(state, start, end); + } + next(); + }, + KeyBlock(node, { state, next }) { + const start = node.start; + const end = state.str.original.indexOf('}', node.expression.end) + 1; + trim_block(state, start, end); + next(); } }; diff --git a/packages/svelte/tests/migrate/samples/remove-blocks-whitespace/input.svelte b/packages/svelte/tests/migrate/samples/remove-blocks-whitespace/input.svelte new file mode 100644 index 0000000000..d1ff0668a8 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/remove-blocks-whitespace/input.svelte @@ -0,0 +1,27 @@ +{ @html "some html" } + +{ #if false && { + +}.x === 34 } + true +{ :else if false } + false +{/if} + +{ #await [] } + { @const x = 43 } + {x} +{ :then i } + {i} +{ :catch e } + dlkdj +{/await} + +{ #await [] then i } +stuff +{/await} + +{ #key count } + dlkdj +{/key} + diff --git a/packages/svelte/tests/migrate/samples/remove-blocks-whitespace/output.svelte b/packages/svelte/tests/migrate/samples/remove-blocks-whitespace/output.svelte new file mode 100644 index 0000000000..e2cb616020 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/remove-blocks-whitespace/output.svelte @@ -0,0 +1,26 @@ +{@html "some html"} + +{#if false && { + +}.x === 34} + true +{:else if false} + false +{/if} + +{#await []} + {@const x = 43} + {x} +{:then i} + {i} +{:catch e} + dlkdj +{/await} + +{#await [] then i} +stuff +{/await} + +{#key count} + dlkdj +{/key} \ No newline at end of file From 5ab65e3d59b678206d1784487b1db049d47ddaec Mon Sep 17 00:00:00 2001 From: george looshch Date: Sat, 26 Oct 2024 17:22:15 +0100 Subject: [PATCH 031/404] docs: consistently capitalize DOM in the Svelte 5 migration guide (#13957) (#13958) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the casing of the word ‘DOM’ in the Svelte 5 migration guide consistent. --- documentation/docs/07-misc/07-v5-migration-guide.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/docs/07-misc/07-v5-migration-guide.md b/documentation/docs/07-misc/07-v5-migration-guide.md index deffca288d..1624755a6b 100644 --- a/documentation/docs/07-misc/07-v5-migration-guide.md +++ b/documentation/docs/07-misc/07-v5-migration-guide.md @@ -319,9 +319,9 @@ When spreading props, local event handlers must go _after_ the spread, or they r > - call said dispatch function with a string and possibly a payload > - retrieve said payload on the other end through a `.details` property, because the event itself was always a `CustomEvent` > -> It was always possible to use component callback props, but because you had to listen to dom events using `on:`, it made sense to use `createEventDispatcher` for component events due to syntactical consistency. Now that we have event attributes (`onclick`), it's the other way around: Callback props are now the more sensible thing to do. +> It was always possible to use component callback props, but because you had to listen to DOM events using `on:`, it made sense to use `createEventDispatcher` for component events due to syntactical consistency. Now that we have event attributes (`onclick`), it's the other way around: Callback props are now the more sensible thing to do. > -> The removal of event modifiers is arguably one of the changes that seems like a step back for those who've liked the shorthand syntax of event modifiers. Given that they are not used that frequently, we traded a smaller surface area for more explicitness. Modifiers also were inconsistent, because most of them were only useable on Dom elements. +> The removal of event modifiers is arguably one of the changes that seems like a step back for those who've liked the shorthand syntax of event modifiers. Given that they are not used that frequently, we traded a smaller surface area for more explicitness. Modifiers also were inconsistent, because most of them were only useable on DOM elements. > > Multiple listeners for the same event are also no longer possible, but it was something of an anti-pattern anyway, since it impedes readability: if there are many attributes, it becomes harder to spot that there are two handlers unless they are right next to each other. It also implies that the two handlers are independent, when in fact something like `event.stopImmediatePropagation()` inside `one` would prevent `two` from being called. > @@ -441,7 +441,7 @@ We thought the same, which is why we provide a migration script to do most of th - bump core dependencies in your `package.json` - migrate to runes (`let` -> `$state` etc) -- migrate to event attributes for Dom elements (`on:click` -> `onclick`) +- migrate to event attributes for DOM elements (`on:click` -> `onclick`) - migrate slot creations to render tags (`` -> `{@render children()}`) - migrate slot usages to snippets (`
...
` -> `{#snippet x()}
...
{/snippet}`) - migrate obvious component creations (`new Component(...)` -> `mount(Component, ...)`) From 67cf3871b4fe4f0130406b6d1c1b51e26f155bb4 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Sat, 26 Oct 2024 19:18:53 +0200 Subject: [PATCH 032/404] fix: more exhaustive check during `SvelteMap.set` in deriveds (#13951) * fix: more exhaustive check during `SvelteMap.set` in deriveds * chore: use set for version reactions * chore: cleanup --- .changeset/sweet-candles-poke.md | 5 +++++ packages/svelte/src/reactivity/map.js | 17 ++++++++++------- .../samples/derived-map/_config.js | 1 - .../samples/derived-map/main.svelte | 4 ++++ 4 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 .changeset/sweet-candles-poke.md diff --git a/.changeset/sweet-candles-poke.md b/.changeset/sweet-candles-poke.md new file mode 100644 index 0000000000..49ec305399 --- /dev/null +++ b/.changeset/sweet-candles-poke.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: more exhaustive check during `SvelteMap.set` in deriveds diff --git a/packages/svelte/src/reactivity/map.js b/packages/svelte/src/reactivity/map.js index ddadad90e2..ae1bf47773 100644 --- a/packages/svelte/src/reactivity/map.js +++ b/packages/svelte/src/reactivity/map.js @@ -1,6 +1,6 @@ /** @import { Source } from '#client' */ import { DEV } from 'esm-env'; -import { source, set } from '../internal/client/reactivity/sources.js'; +import { set, source } from '../internal/client/reactivity/sources.js'; import { get } from '../internal/client/runtime.js'; import { increment } from './utils.js'; @@ -102,12 +102,15 @@ export class SvelteMap extends Map { increment(version); } else if (prev_res !== value) { increment(s); - // If no one listening to this property and is listening to the version, or - // the inverse, then we should increment the version to be safe - if ( - (s.reactions === null && version.reactions !== null) || - (s.reactions !== null && version.reactions === null) - ) { + + // if not every reaction of s is a reaction of version we need to also include version + var v_reactions = version.reactions === null ? null : new Set(version.reactions); + var needs_version_increase = + v_reactions === null || + !s.reactions?.every((r) => + /** @type {NonNullable} */ (v_reactions).has(r) + ); + if (needs_version_increase) { increment(version); } } diff --git a/packages/svelte/tests/runtime-runes/samples/derived-map/_config.js b/packages/svelte/tests/runtime-runes/samples/derived-map/_config.js index f3d115c62b..f933d0b2fa 100644 --- a/packages/svelte/tests/runtime-runes/samples/derived-map/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/derived-map/_config.js @@ -3,7 +3,6 @@ import { test } from '../../test'; export default test({ html: `Loading`, - async test({ assert, target }) { await Promise.resolve(); flushSync(); diff --git a/packages/svelte/tests/runtime-runes/samples/derived-map/main.svelte b/packages/svelte/tests/runtime-runes/samples/derived-map/main.svelte index 6278af8158..ea51f29dfb 100644 --- a/packages/svelte/tests/runtime-runes/samples/derived-map/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/derived-map/main.svelte @@ -24,6 +24,10 @@ } const value = $derived(get_async(1)); + const value2 = $derived(get_async(1)); + // both values are read before the set + value; + value2; {#if value instanceof Promise} From ac6d8c0bf4534221818c634286c7c9b1b7fd91d3 Mon Sep 17 00:00:00 2001 From: Max Duval Date: Sun, 27 Oct 2024 10:36:36 +0000 Subject: [PATCH 033/404] docs: fix small typo (#13967) A full stop or colon, we myst choose --- documentation/docs/03-template-syntax/12-use.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/03-template-syntax/12-use.md b/documentation/docs/03-template-syntax/12-use.md index ff50e38cf2..f3db72a772 100644 --- a/documentation/docs/03-template-syntax/12-use.md +++ b/documentation/docs/03-template-syntax/12-use.md @@ -45,7 +45,7 @@ The action is only called once (but not during server-side rendering) — it wil ## Typing -The `Action` interface receives three optional type arguments — a node type (which can be `Element`, if the action applies to everything), a parameter, and any custom event handlers created by the action.: +The `Action` interface receives three optional type arguments — a node type (which can be `Element`, if the action applies to everything), a parameter, and any custom event handlers created by the action: ```svelte From 430c2bb129214aed17ca27db8bef5c9e9c897428 Mon Sep 17 00:00:00 2001 From: Tee Ming Date: Mon, 28 Oct 2024 15:38:58 +0800 Subject: [PATCH 034/404] docs: use `$effect.pre` instead of `beforeUpdate` (#13955) * Update 03-lifecycle-hooks.md * Update documentation/docs/06-runtime/03-lifecycle-hooks.md Co-authored-by: Paolo Ricciuti --------- Co-authored-by: Paolo Ricciuti --- documentation/docs/06-runtime/03-lifecycle-hooks.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/documentation/docs/06-runtime/03-lifecycle-hooks.md b/documentation/docs/06-runtime/03-lifecycle-hooks.md index 56ce8e1cdd..c8699e1f6d 100644 --- a/documentation/docs/06-runtime/03-lifecycle-hooks.md +++ b/documentation/docs/06-runtime/03-lifecycle-hooks.md @@ -67,12 +67,13 @@ While there's no "after update" hook, you can use `tick` to ensure that the UI i ```svelte ``` From e99e865291dd135df2d6f5f1836f3364afb10e5d Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 28 Oct 2024 21:05:24 +0900 Subject: [PATCH 035/404] docs: update links to point to new site (#13986) Update (some) of the links pointing to the old preview site as mentioned in #13982. This updates all the links in documentation/ and packages/svelte/messages, but doesn't touch others present in packages/svelte/ or fix the original link in that issue. --- documentation/docs/03-template-syntax/01-basic-markup.md | 2 +- .../docs/98-reference/.generated/client-errors.md | 4 ++-- .../docs/98-reference/.generated/client-warnings.md | 2 +- packages/svelte/messages/client-errors/errors.md | 4 ++-- packages/svelte/messages/client-warnings/warnings.md | 2 +- packages/svelte/src/internal/client/errors.js | 8 ++++---- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/documentation/docs/03-template-syntax/01-basic-markup.md b/documentation/docs/03-template-syntax/01-basic-markup.md index 774ac6ca3e..b41dc187c3 100644 --- a/documentation/docs/03-template-syntax/01-basic-markup.md +++ b/documentation/docs/03-template-syntax/01-basic-markup.md @@ -109,7 +109,7 @@ Timing-wise, event attributes always fire after events from bindings (e.g. `onin When using `ontouchstart` and `ontouchmove` event attributes, the handlers are [passive](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#using_passive_listeners) for better performance. 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()`. -In the very rare cases that you need to prevent these event defaults, you should use [`on`](https://svelte-5-preview.vercel.app/docs/imports#svelte-events) instead (for example inside an action). +In the very rare cases that you need to prevent these event defaults, you should use [`on`](svelte-events#on) instead (for example inside an action). ### Event delegation diff --git a/documentation/docs/98-reference/.generated/client-errors.md b/documentation/docs/98-reference/.generated/client-errors.md index d6bd276de7..9879b67b33 100644 --- a/documentation/docs/98-reference/.generated/client-errors.md +++ b/documentation/docs/98-reference/.generated/client-errors.md @@ -19,13 +19,13 @@ 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-5-preview.vercel.app/docs/breaking-changes#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 https://svelte.dev/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-5-preview.vercel.app/docs/breaking-changes#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 https://svelte.dev/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 2eabff6958..743a47b67a 100644 --- a/documentation/docs/98-reference/.generated/client-warnings.md +++ b/documentation/docs/98-reference/.generated/client-warnings.md @@ -16,7 +16,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-5-preview.vercel.app/docs/runes#$inspect) rune. Alternatively, to log things on a one-off basis (for example, inside an event handler) you can use [`$state.snapshot`](https://svelte-5-preview.vercel.app/docs/runes#$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`](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. ### event_handler_invalid diff --git a/packages/svelte/messages/client-errors/errors.md b/packages/svelte/messages/client-errors/errors.md index 954214cb81..517b028fa2 100644 --- a/packages/svelte/messages/client-errors/errors.md +++ b/packages/svelte/messages/client-errors/errors.md @@ -12,11 +12,11 @@ ## component_api_changed -> %parent% called `%method%` on an instance of %component%, which is no longer valid in Svelte 5. See https://svelte-5-preview.vercel.app/docs/breaking-changes#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 https://svelte.dev/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-5-preview.vercel.app/docs/breaking-changes#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 https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes for more information ## derived_references_self diff --git a/packages/svelte/messages/client-warnings/warnings.md b/packages/svelte/messages/client-warnings/warnings.md index c6f1b0aae8..e9014207fd 100644 --- a/packages/svelte/messages/client-warnings/warnings.md +++ b/packages/svelte/messages/client-warnings/warnings.md @@ -10,7 +10,7 @@ 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-5-preview.vercel.app/docs/runes#$inspect) rune. Alternatively, to log things on a one-off basis (for example, inside an event handler) you can use [`$state.snapshot`](https://svelte-5-preview.vercel.app/docs/runes#$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`](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. ## event_handler_invalid diff --git a/packages/svelte/src/internal/client/errors.js b/packages/svelte/src/internal/client/errors.js index 2a8f0e8914..2d95c42365 100644 --- a/packages/svelte/src/internal/client/errors.js +++ b/packages/svelte/src/internal/client/errors.js @@ -57,7 +57,7 @@ export function bind_not_bindable(key, component, name) { } /** - * %parent% called `%method%` on an instance of %component%, which is no longer valid in Svelte 5. See https://svelte-5-preview.vercel.app/docs/breaking-changes#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 https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes for more information * @param {string} parent * @param {string} method * @param {string} component @@ -65,7 +65,7 @@ export function bind_not_bindable(key, component, name) { */ export function component_api_changed(parent, 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. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more information`); + const error = new Error(`component_api_changed\n${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`); error.name = 'Svelte error'; throw error; @@ -76,14 +76,14 @@ export function component_api_changed(parent, method, component) { } /** - * 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-5-preview.vercel.app/docs/breaking-changes#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 https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes for more information * @param {string} component * @param {string} name * @returns {never} */ export function component_api_invalid_new(component, name) { if (DEV) { - const error = new Error(`component_api_invalid_new\nAttempted 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-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more information`); + const error = new Error(`component_api_invalid_new\nAttempted 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`); error.name = 'Svelte error'; throw error; From 777d059b10ded7e0e4af0cfb837c9e00864b3ec2 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 28 Oct 2024 13:28:21 -0400 Subject: [PATCH 036/404] docs: update heading level in `02-getting-started.md` (#13997) --- documentation/docs/01-introduction/02-getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/01-introduction/02-getting-started.md b/documentation/docs/01-introduction/02-getting-started.md index 1abc05fc5e..37757d961f 100644 --- a/documentation/docs/01-introduction/02-getting-started.md +++ b/documentation/docs/01-introduction/02-getting-started.md @@ -13,7 +13,7 @@ npm run dev Don't worry if you don't know Svelte yet! You can ignore all the nice features SvelteKit brings on top for now and dive into it later. -### Alternatives to SvelteKit +## 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. From 3876b302f3bbaedfae1fa20e334354e791f2dbc6 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Mon, 28 Oct 2024 22:03:59 +0000 Subject: [PATCH 037/404] fix: ensure effect_tracking correctly handles tracking reactions (#14005) * fix: ensure effect_tracking correctly handles tracking reactions * fix: ensure effect_tracking correctly handles tracking reactions --- .changeset/clean-badgers-battle.md | 5 +++++ .../src/internal/client/reactivity/effects.js | 7 +++++-- .../samples/derived-unowned-10/_config.js | 19 +++++++++++++++++++ .../samples/derived-unowned-10/main.svelte | 13 +++++++++++++ .../derived-unowned-10/theme.svelte.js | 18 ++++++++++++++++++ 5 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 .changeset/clean-badgers-battle.md create mode 100644 packages/svelte/tests/runtime-runes/samples/derived-unowned-10/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/derived-unowned-10/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/derived-unowned-10/theme.svelte.js diff --git a/.changeset/clean-badgers-battle.md b/.changeset/clean-badgers-battle.md new file mode 100644 index 0000000000..be9b7de2cb --- /dev/null +++ b/.changeset/clean-badgers-battle.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure effect_tracking correctly handles tracking reactions diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 2050c142c5..82baeae288 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -15,7 +15,8 @@ import { set_is_destroying_effect, set_is_flushing_effect, set_signal_status, - untrack + untrack, + skip_reaction } from '../runtime.js'; import { DIRTY, @@ -167,7 +168,9 @@ export function effect_tracking() { return false; } - return (active_reaction.f & UNOWNED) === 0; + // If it's skipped, that's because we're inside an unowned + // that is not being tracked by another reaction + return !skip_reaction; } /** diff --git a/packages/svelte/tests/runtime-runes/samples/derived-unowned-10/_config.js b/packages/svelte/tests/runtime-runes/samples/derived-unowned-10/_config.js new file mode 100644 index 0000000000..7e057e94b2 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/derived-unowned-10/_config.js @@ -0,0 +1,19 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target, logs }) { + let btn1 = target.querySelector('button'); + + btn1?.click(); + flushSync(); + + btn1?.click(); + flushSync(); + + btn1?.click(); + flushSync(); + + assert.deepEqual(logs, ['light', 'dark', 'light']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/derived-unowned-10/main.svelte b/packages/svelte/tests/runtime-runes/samples/derived-unowned-10/main.svelte new file mode 100644 index 0000000000..d35c706aec --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/derived-unowned-10/main.svelte @@ -0,0 +1,13 @@ + + + + +{themeState.value.theme} \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/derived-unowned-10/theme.svelte.js b/packages/svelte/tests/runtime-runes/samples/derived-unowned-10/theme.svelte.js new file mode 100644 index 0000000000..30c187cbe9 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/derived-unowned-10/theme.svelte.js @@ -0,0 +1,18 @@ +import { fromStore, writable } from 'svelte/store'; + +export const store = writable({ theme: 'dark' }); + +class ThemeState { + #storeState = fromStore(store); + value = $derived(this.#storeState.current); + + constructor() { + $effect.root(() => { + $effect(() => { + console.log(this.value.theme); + }); + }); + } +} + +export const themeState = new ThemeState(); From b6fccdb4f7ccbfe5e1548646f09c68f28e96783c Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Mon, 28 Oct 2024 23:17:02 +0100 Subject: [PATCH 038/404] fix: properly migrate imports types prefixed with $ (#14007) --- .changeset/wild-oranges-promise.md | 5 +++++ .../phases/2-analyze/visitors/shared/utils.js | 11 ++++++++++- packages/svelte/src/compiler/phases/scope.js | 2 +- .../migrate/samples/import-type-$-prefix/input.svelte | 5 +++++ .../samples/import-type-$-prefix/output.svelte | 9 +++++++++ 5 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 .changeset/wild-oranges-promise.md create mode 100644 packages/svelte/tests/migrate/samples/import-type-$-prefix/input.svelte create mode 100644 packages/svelte/tests/migrate/samples/import-type-$-prefix/output.svelte diff --git a/.changeset/wild-oranges-promise.md b/.changeset/wild-oranges-promise.md new file mode 100644 index 0000000000..d79666c47e --- /dev/null +++ b/.changeset/wild-oranges-promise.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: properly migrate imports types prefixed with $ 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 266016fcc8..82356ea619 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 @@ -220,7 +220,16 @@ export function validate_identifier_name(binding, function_depth) { if (node.name === '$') { e.dollar_binding_invalid(node); - } else if (node.name.startsWith('$')) { + } else if ( + node.name.startsWith('$') && + // import type { $Type } from "" - these are normally already filtered out, + // but for the migration they aren't, and throwing here is preventing the migration to complete + // TODO -> once migration script is gone we can remove this check + !( + binding.initial?.type === 'ImportDeclaration' && + /** @type {any} */ (binding.initial).importKind === 'type' + ) + ) { e.dollar_prefix_invalid(node); } } diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index 7a8afa532a..0542a20708 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -348,7 +348,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) { is_reference(node, /** @type {Node} */ (parent)) && // TSTypeAnnotation, TSInterfaceDeclaration etc - these are normally already filtered out, // but for the migration they aren't, so we need to filter them out here - // -> once migration script is gone we can remove this check + // TODO -> once migration script is gone we can remove this check !parent.type.startsWith('TS') ) { references.push([state.scope, { node, path: path.slice() }]); diff --git a/packages/svelte/tests/migrate/samples/import-type-$-prefix/input.svelte b/packages/svelte/tests/migrate/samples/import-type-$-prefix/input.svelte new file mode 100644 index 0000000000..25eb04f319 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/import-type-$-prefix/input.svelte @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/import-type-$-prefix/output.svelte b/packages/svelte/tests/migrate/samples/import-type-$-prefix/output.svelte new file mode 100644 index 0000000000..bcd724d8ba --- /dev/null +++ b/packages/svelte/tests/migrate/samples/import-type-$-prefix/output.svelte @@ -0,0 +1,9 @@ + \ No newline at end of file From c93a645b68659c5d1a6f2851b9f115285c146805 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Mon, 28 Oct 2024 15:59:39 -0700 Subject: [PATCH 039/404] docs: fix broken links (#13944) --- .changeset/soft-snails-retire.md | 5 +++++ packages/svelte/src/index-client.js | 4 ++-- packages/svelte/types/index.d.ts | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 .changeset/soft-snails-retire.md diff --git a/.changeset/soft-snails-retire.md b/.changeset/soft-snails-retire.md new file mode 100644 index 0000000000..2d8a7efb6a --- /dev/null +++ b/.changeset/soft-snails-retire.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: update broken links diff --git a/packages/svelte/src/index-client.js b/packages/svelte/src/index-client.js index 2916aaecb1..1b6175c948 100644 --- a/packages/svelte/src/index-client.js +++ b/packages/svelte/src/index-client.js @@ -14,7 +14,7 @@ import { lifecycle_outside_component } from './internal/shared/errors.js'; * * If a function is returned _synchronously_ from `onMount`, it will be called when the component is unmounted. * - * `onMount` does not run inside a [server-side component](https://svelte.dev/docs#run-time-server-side-component-api). + * `onMount` does not run inside [server-side components](https://svelte.dev/docs/svelte/svelte-server#render). * * @template T * @param {() => NotFunction | Promise> | (() => any)} fn @@ -64,7 +64,7 @@ function create_custom_event(type, detail, { bubbles = false, cancelable = false } /** - * Creates an event dispatcher that can be used to dispatch [component events](https://svelte.dev/docs#template-syntax-component-directives-on-eventname). + * Creates an event dispatcher that can be used to dispatch [component events](https://svelte.dev/docs/svelte/legacy-on#Component-events). * Event dispatchers are functions that can take two arguments: `name` and `detail`. * * Component events created with `createEventDispatcher` create a diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index c7794d5b0e..c07eeedb37 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -354,7 +354,7 @@ declare module 'svelte' { * * If a function is returned _synchronously_ from `onMount`, it will be called when the component is unmounted. * - * `onMount` does not run inside a [server-side component](https://svelte.dev/docs#run-time-server-side-component-api). + * `onMount` does not run inside [server-side components](https://svelte.dev/docs/svelte/svelte-server#render). * * */ export function onMount(fn: () => NotFunction | Promise> | (() => any)): void; @@ -367,7 +367,7 @@ declare module 'svelte' { * */ export function onDestroy(fn: () => any): void; /** - * Creates an event dispatcher that can be used to dispatch [component events](https://svelte.dev/docs#template-syntax-component-directives-on-eventname). + * Creates an event dispatcher that can be used to dispatch [component events](https://svelte.dev/docs/svelte/legacy-on#Component-events). * Event dispatchers are functions that can take two arguments: `name` and `detail`. * * Component events created with `createEventDispatcher` create a From 07219f1cbd3a0f1b3bfe85ad9f3c8c61324a02a7 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Mon, 28 Oct 2024 16:00:01 -0700 Subject: [PATCH 040/404] chore: update links that previously pointed to preview site (#14001) --- .changeset/tiny-boxes-exist.md | 5 ++ packages/svelte/src/ambient.d.ts | 26 ++++----- .../3-transform/server/transform-server.js | 2 +- packages/svelte/src/index-client.js | 6 +-- packages/svelte/src/index.d.ts | 20 +++---- .../svelte/src/internal/client/runtime.js | 2 - packages/svelte/types/index.d.ts | 54 +++++++++---------- 7 files changed, 58 insertions(+), 57 deletions(-) create mode 100644 .changeset/tiny-boxes-exist.md diff --git a/.changeset/tiny-boxes-exist.md b/.changeset/tiny-boxes-exist.md new file mode 100644 index 0000000000..67286571f2 --- /dev/null +++ b/.changeset/tiny-boxes-exist.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: update links that previously pointed to preview site diff --git a/packages/svelte/src/ambient.d.ts b/packages/svelte/src/ambient.d.ts index 6af8ca5156..88879394ed 100644 --- a/packages/svelte/src/ambient.d.ts +++ b/packages/svelte/src/ambient.d.ts @@ -24,7 +24,7 @@ declare module '*.svelte' { * let count = $state(0); * ``` * - * https://svelte-5-preview.vercel.app/docs/runes#$state + * https://svelte.dev/docs/svelte/$state * * @param initial The initial value */ @@ -120,7 +120,7 @@ declare namespace $state { * * ``` * - * https://svelte-5-preview.vercel.app/docs/runes#$state-raw + * https://svelte.dev/docs/svelte/$state#$state.raw * * @param initial The initial value */ @@ -141,7 +141,7 @@ declare namespace $state { * * ``` * - * https://svelte-5-preview.vercel.app/docs/runes#$state.snapshot + * https://svelte.dev/docs/svelte/$state#$state.snapshot * * @param state The value to snapshot */ @@ -178,7 +178,7 @@ declare namespace $state { * let double = $derived(count * 2); * ``` * - * https://svelte-5-preview.vercel.app/docs/runes#$derived + * https://svelte.dev/docs/svelte/$derived * * @param expression The derived state expression */ @@ -200,7 +200,7 @@ declare namespace $derived { * }); * ``` * - * https://svelte-5-preview.vercel.app/docs/runes#$derived-by + * https://svelte.dev/docs/svelte/$derived#$derived.by */ export function by(fn: () => T): T; @@ -239,7 +239,7 @@ declare namespace $derived { * * Does not run during server side rendering. * - * https://svelte-5-preview.vercel.app/docs/runes#$effect + * https://svelte.dev/docs/svelte/$effect * @param fn The function to execute */ declare function $effect(fn: () => void | (() => void)): void; @@ -258,7 +258,7 @@ declare namespace $effect { * * Does not run during server side rendering. * - * https://svelte-5-preview.vercel.app/docs/runes#$effect-pre + * https://svelte.dev/docs/svelte/$effect#$effect.pre * @param fn The function to execute */ export function pre(fn: () => void | (() => void)): void; @@ -281,7 +281,7 @@ declare namespace $effect { * * This allows you to (for example) add things like subscriptions without causing memory leaks, by putting them in child effects. * - * https://svelte-5-preview.vercel.app/docs/runes#$effect-tracking + * https://svelte.dev/docs/svelte/$effect#$effect.tracking */ export function tracking(): boolean; @@ -309,7 +309,7 @@ declare namespace $effect { * * ``` * - * https://svelte-5-preview.vercel.app/docs/runes#$effect-root + * https://svelte.dev/docs/svelte/$effect#$effect.root */ export function root(fn: () => void | (() => void)): () => void; @@ -342,7 +342,7 @@ declare namespace $effect { * let { optionalProp = 42, requiredProp, bindableProp = $bindable() }: { optionalProp?: number; requiredProps: string; bindableProp: boolean } = $props(); * ``` * - * https://svelte-5-preview.vercel.app/docs/runes#$props + * https://svelte.dev/docs/svelte/$props */ declare function $props(): any; @@ -353,7 +353,7 @@ declare function $props(): any; * let { propName = $bindable() }: { propName: boolean } = $props(); * ``` * - * https://svelte-5-preview.vercel.app/docs/runes#$bindable + * https://svelte.dev/docs/svelte/$bindable */ declare function $bindable(fallback?: T): T; @@ -373,7 +373,7 @@ declare function $bindable(fallback?: T): T; * $inspect(x, y).with(() => { debugger; }); * ``` * - * https://svelte-5-preview.vercel.app/docs/runes#$inspect + * https://svelte.dev/docs/svelte/$inspect */ declare function $inspect( ...values: T @@ -396,6 +396,6 @@ declare function $inspect( * * Only available inside custom element components, and only on the client-side. * - * https://svelte-5-preview.vercel.app/docs/runes#$host + * https://svelte.dev/docs/svelte/$host */ declare function $host(): El; 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 5735e94ca4..c164aa4219 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 @@ -344,7 +344,7 @@ export function server_component(analysis, options) { b.block([ b.throw_error( `Component.render(...) is no longer valid in Svelte 5. ` + - 'See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more information' + 'See https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes for more information' ) ]) ) diff --git a/packages/svelte/src/index-client.js b/packages/svelte/src/index-client.js index 1b6175c948..ff477595a1 100644 --- a/packages/svelte/src/index-client.js +++ b/packages/svelte/src/index-client.js @@ -82,7 +82,7 @@ function create_custom_event(type, detail, { bubbles = false, cancelable = false * }>(); * ``` * - * @deprecated Use callback props and/or the `$host()` rune instead — see https://svelte-5-preview.vercel.app/docs/deprecations#createeventdispatcher + * @deprecated Use callback props and/or the `$host()` rune instead — see https://svelte.dev/docs/svelte/v5-migration-guide#Event-changes-Component-events * @template {Record} [EventMap = any] * @returns {EventDispatcher} */ @@ -121,7 +121,7 @@ export function createEventDispatcher() { * * In runes mode use `$effect.pre` instead. * - * @deprecated Use `$effect.pre` instead — see https://svelte-5-preview.vercel.app/docs/deprecations#beforeupdate-and-afterupdate + * @deprecated Use `$effect.pre` instead — see https://svelte.dev/docs/svelte/$effect#$effect.pre * @param {() => void} fn * @returns {void} */ @@ -144,7 +144,7 @@ export function beforeUpdate(fn) { * * In runes mode use `$effect` instead. * - * @deprecated Use `$effect` instead — see https://svelte-5-preview.vercel.app/docs/deprecations#beforeupdate-and-afterupdate + * @deprecated Use `$effect` instead — see https://svelte.dev/docs/svelte/$effect * @param {() => void} fn * @returns {void} */ diff --git a/packages/svelte/src/index.d.ts b/packages/svelte/src/index.d.ts index 76ea112cd2..5130721cbe 100644 --- a/packages/svelte/src/index.d.ts +++ b/packages/svelte/src/index.d.ts @@ -5,7 +5,7 @@ import './ambient.js'; /** * @deprecated In Svelte 4, components are classes. In Svelte 5, they are functions. * Use `mount` instead to instantiate components. - * See [breaking changes](https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes) + * See [migration guide](https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes) * for more info. */ export interface ComponentConstructorOptions< @@ -39,7 +39,7 @@ type Properties = Props & * This was the base class for Svelte components in Svelte 4. Svelte 5+ components * are completely different under the hood. For typing, use `Component` instead. * To instantiate components, use `mount` instead`. - * See [breaking changes documentation](https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes) for more info. + * See [migration guide](https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes) for more info. */ export class SvelteComponent< Props extends Record = Record, @@ -53,7 +53,7 @@ export class SvelteComponent< /** * @deprecated This constructor only exists when using the `asClassComponent` compatibility helper, which * is a stop-gap solution. Migrate towards using `mount` instead. See - * https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more info. + * https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes for more info. */ constructor(options: ComponentConstructorOptions>); /** @@ -83,14 +83,14 @@ export class SvelteComponent< /** * @deprecated This method only exists when using one of the legacy compatibility helpers, which - * is a stop-gap solution. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes + * is a stop-gap solution. See https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes * for more info. */ $destroy(): void; /** * @deprecated This method only exists when using one of the legacy compatibility helpers, which - * is a stop-gap solution. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes + * is a stop-gap solution. See https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes * for more info. */ $on>( @@ -100,7 +100,7 @@ export class SvelteComponent< /** * @deprecated This method only exists when using one of the legacy compatibility helpers, which - * is a stop-gap solution. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes + * is a stop-gap solution. See https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes * for more info. */ $set(props: Partial): void; @@ -153,13 +153,13 @@ export interface Component< ): { /** * @deprecated This method only exists when using one of the legacy compatibility helpers, which - * is a stop-gap solution. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes + * is a stop-gap solution. See https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes * for more info. */ $on?(type: string, callback: (e: any) => void): () => void; /** * @deprecated This method only exists when using one of the legacy compatibility helpers, which - * is a stop-gap solution. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes + * is a stop-gap solution. See https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes * for more info. */ $set?(props: Partial): void; @@ -171,7 +171,7 @@ export interface Component< } /** - * @deprecated Use `Component` instead. See [breaking changes documentation](https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes) for more information. + * @deprecated Use `Component` instead. See [migration guide](https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes) for more information. */ export class SvelteComponentTyped< Props extends Record = Record, @@ -278,7 +278,7 @@ declare const SnippetReturn: unique symbol; * ``` * You can only call a snippet through the `{@render ...}` tag. * - * https://svelte-5-preview.vercel.app/docs/snippets + * https://svelte.dev/docs/svelte/snippet * * @template Parameters the parameters that the snippet expects (if any) as a tuple. */ diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 074c81588a..26c8d9adbb 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -832,8 +832,6 @@ export function invalidate_inner_signals(fn) { /** * Use `untrack` to prevent something from being treated as an `$effect`/`$derived` dependency. - * - * https://svelte-5-preview.vercel.app/docs/functions#untrack * @template T * @param {() => T} fn * @returns {T} diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index c07eeedb37..ebf17db6e2 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -2,7 +2,7 @@ declare module 'svelte' { /** * @deprecated In Svelte 4, components are classes. In Svelte 5, they are functions. * Use `mount` instead to instantiate components. - * See [breaking changes](https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes) + * See [migration guide](https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes) * for more info. */ export interface ComponentConstructorOptions< @@ -36,7 +36,7 @@ declare module 'svelte' { * This was the base class for Svelte components in Svelte 4. Svelte 5+ components * are completely different under the hood. For typing, use `Component` instead. * To instantiate components, use `mount` instead`. - * See [breaking changes documentation](https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes) for more info. + * See [migration guide](https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes) for more info. */ export class SvelteComponent< Props extends Record = Record, @@ -50,7 +50,7 @@ declare module 'svelte' { /** * @deprecated This constructor only exists when using the `asClassComponent` compatibility helper, which * is a stop-gap solution. Migrate towards using `mount` instead. See - * https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more info. + * https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes for more info. */ constructor(options: ComponentConstructorOptions>); /** @@ -80,14 +80,14 @@ declare module 'svelte' { /** * @deprecated This method only exists when using one of the legacy compatibility helpers, which - * is a stop-gap solution. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes + * is a stop-gap solution. See https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes * for more info. */ $destroy(): void; /** * @deprecated This method only exists when using one of the legacy compatibility helpers, which - * is a stop-gap solution. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes + * is a stop-gap solution. See https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes * for more info. */ $on>( @@ -97,7 +97,7 @@ declare module 'svelte' { /** * @deprecated This method only exists when using one of the legacy compatibility helpers, which - * is a stop-gap solution. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes + * is a stop-gap solution. See https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes * for more info. */ $set(props: Partial): void; @@ -150,13 +150,13 @@ declare module 'svelte' { ): { /** * @deprecated This method only exists when using one of the legacy compatibility helpers, which - * is a stop-gap solution. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes + * is a stop-gap solution. See https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes * for more info. */ $on?(type: string, callback: (e: any) => void): () => void; /** * @deprecated This method only exists when using one of the legacy compatibility helpers, which - * is a stop-gap solution. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes + * is a stop-gap solution. See https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes * for more info. */ $set?(props: Partial): void; @@ -168,7 +168,7 @@ declare module 'svelte' { } /** - * @deprecated Use `Component` instead. See [breaking changes documentation](https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes) for more information. + * @deprecated Use `Component` instead. See [migration guide](https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes) for more information. */ export class SvelteComponentTyped< Props extends Record = Record, @@ -275,7 +275,7 @@ declare module 'svelte' { * ``` * You can only call a snippet through the `{@render ...}` tag. * - * https://svelte-5-preview.vercel.app/docs/snippets + * https://svelte.dev/docs/svelte/snippet * * @template Parameters the parameters that the snippet expects (if any) as a tuple. */ @@ -385,7 +385,7 @@ declare module 'svelte' { * }>(); * ``` * - * @deprecated Use callback props and/or the `$host()` rune instead — see https://svelte-5-preview.vercel.app/docs/deprecations#createeventdispatcher + * @deprecated Use callback props and/or the `$host()` rune instead — see https://svelte.dev/docs/svelte/v5-migration-guide#Event-changes-Component-events * */ export function createEventDispatcher = any>(): EventDispatcher; /** @@ -395,7 +395,7 @@ declare module 'svelte' { * * In runes mode use `$effect.pre` instead. * - * @deprecated Use `$effect.pre` instead — see https://svelte-5-preview.vercel.app/docs/deprecations#beforeupdate-and-afterupdate + * @deprecated Use `$effect.pre` instead — see https://svelte.dev/docs/svelte/$effect#$effect.pre * */ export function beforeUpdate(fn: () => void): void; /** @@ -405,7 +405,7 @@ declare module 'svelte' { * * In runes mode use `$effect` instead. * - * @deprecated Use `$effect` instead — see https://svelte-5-preview.vercel.app/docs/deprecations#beforeupdate-and-afterupdate + * @deprecated Use `$effect` instead — see https://svelte.dev/docs/svelte/$effect * */ export function afterUpdate(fn: () => void): void; /** @@ -456,8 +456,6 @@ declare module 'svelte' { export function tick(): Promise; /** * Use `untrack` to prevent something from being treated as an `$effect`/`$derived` dependency. - * - * https://svelte-5-preview.vercel.app/docs/functions#untrack * */ export function untrack(fn: () => T): T; /** @@ -2322,7 +2320,7 @@ declare module 'svelte/types/compiler/interfaces' { * let count = $state(0); * ``` * - * https://svelte-5-preview.vercel.app/docs/runes#$state + * https://svelte.dev/docs/svelte/$state * * @param initial The initial value */ @@ -2418,7 +2416,7 @@ declare namespace $state { * * ``` * - * https://svelte-5-preview.vercel.app/docs/runes#$state-raw + * https://svelte.dev/docs/svelte/$state#$state.raw * * @param initial The initial value */ @@ -2439,7 +2437,7 @@ declare namespace $state { * * ``` * - * https://svelte-5-preview.vercel.app/docs/runes#$state.snapshot + * https://svelte.dev/docs/svelte/$state#$state.snapshot * * @param state The value to snapshot */ @@ -2476,7 +2474,7 @@ declare namespace $state { * let double = $derived(count * 2); * ``` * - * https://svelte-5-preview.vercel.app/docs/runes#$derived + * https://svelte.dev/docs/svelte/$derived * * @param expression The derived state expression */ @@ -2498,7 +2496,7 @@ declare namespace $derived { * }); * ``` * - * https://svelte-5-preview.vercel.app/docs/runes#$derived-by + * https://svelte.dev/docs/svelte/$derived#$derived.by */ export function by(fn: () => T): T; @@ -2537,7 +2535,7 @@ declare namespace $derived { * * Does not run during server side rendering. * - * https://svelte-5-preview.vercel.app/docs/runes#$effect + * https://svelte.dev/docs/svelte/$effect * @param fn The function to execute */ declare function $effect(fn: () => void | (() => void)): void; @@ -2556,7 +2554,7 @@ declare namespace $effect { * * Does not run during server side rendering. * - * https://svelte-5-preview.vercel.app/docs/runes#$effect-pre + * https://svelte.dev/docs/svelte/$effect#$effect.pre * @param fn The function to execute */ export function pre(fn: () => void | (() => void)): void; @@ -2579,7 +2577,7 @@ declare namespace $effect { * * This allows you to (for example) add things like subscriptions without causing memory leaks, by putting them in child effects. * - * https://svelte-5-preview.vercel.app/docs/runes#$effect-tracking + * https://svelte.dev/docs/svelte/$effect#$effect.tracking */ export function tracking(): boolean; @@ -2607,7 +2605,7 @@ declare namespace $effect { * * ``` * - * https://svelte-5-preview.vercel.app/docs/runes#$effect-root + * https://svelte.dev/docs/svelte/$effect#$effect.root */ export function root(fn: () => void | (() => void)): () => void; @@ -2640,7 +2638,7 @@ declare namespace $effect { * let { optionalProp = 42, requiredProp, bindableProp = $bindable() }: { optionalProp?: number; requiredProps: string; bindableProp: boolean } = $props(); * ``` * - * https://svelte-5-preview.vercel.app/docs/runes#$props + * https://svelte.dev/docs/svelte/$props */ declare function $props(): any; @@ -2651,7 +2649,7 @@ declare function $props(): any; * let { propName = $bindable() }: { propName: boolean } = $props(); * ``` * - * https://svelte-5-preview.vercel.app/docs/runes#$bindable + * https://svelte.dev/docs/svelte/$bindable */ declare function $bindable(fallback?: T): T; @@ -2671,7 +2669,7 @@ declare function $bindable(fallback?: T): T; * $inspect(x, y).with(() => { debugger; }); * ``` * - * https://svelte-5-preview.vercel.app/docs/runes#$inspect + * https://svelte.dev/docs/svelte/$inspect */ declare function $inspect( ...values: T @@ -2694,7 +2692,7 @@ declare function $inspect( * * Only available inside custom element components, and only on the client-side. * - * https://svelte-5-preview.vercel.app/docs/runes#$host + * https://svelte.dev/docs/svelte/$host */ declare function $host(): El; From 444195202835651501f7317dda47154aa5d51ba0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 28 Oct 2024 16:01:52 -0700 Subject: [PATCH 041/404] Version Packages (#13949) Co-authored-by: github-actions[bot] --- .changeset/brave-fans-sneeze.md | 5 ----- .changeset/clean-badgers-battle.md | 5 ----- .changeset/soft-snails-retire.md | 5 ----- .changeset/sweet-candles-poke.md | 5 ----- .changeset/tasty-frogs-boil.md | 5 ----- .changeset/tiny-boxes-exist.md | 5 ----- .changeset/wild-oranges-promise.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/brave-fans-sneeze.md delete mode 100644 .changeset/clean-badgers-battle.md delete mode 100644 .changeset/soft-snails-retire.md delete mode 100644 .changeset/sweet-candles-poke.md delete mode 100644 .changeset/tasty-frogs-boil.md delete mode 100644 .changeset/tiny-boxes-exist.md delete mode 100644 .changeset/wild-oranges-promise.md diff --git a/.changeset/brave-fans-sneeze.md b/.changeset/brave-fans-sneeze.md deleted file mode 100644 index c5fe894acb..0000000000 --- a/.changeset/brave-fans-sneeze.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: add empty stack to `CompileDiagnostic` to show error on build diff --git a/.changeset/clean-badgers-battle.md b/.changeset/clean-badgers-battle.md deleted file mode 100644 index be9b7de2cb..0000000000 --- a/.changeset/clean-badgers-battle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure effect_tracking correctly handles tracking reactions diff --git a/.changeset/soft-snails-retire.md b/.changeset/soft-snails-retire.md deleted file mode 100644 index 2d8a7efb6a..0000000000 --- a/.changeset/soft-snails-retire.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"svelte": patch ---- - -fix: update broken links diff --git a/.changeset/sweet-candles-poke.md b/.changeset/sweet-candles-poke.md deleted file mode 100644 index 49ec305399..0000000000 --- a/.changeset/sweet-candles-poke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: more exhaustive check during `SvelteMap.set` in deriveds diff --git a/.changeset/tasty-frogs-boil.md b/.changeset/tasty-frogs-boil.md deleted file mode 100644 index 26b33db998..0000000000 --- a/.changeset/tasty-frogs-boil.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: trim whitespace while migrating blocks diff --git a/.changeset/tiny-boxes-exist.md b/.changeset/tiny-boxes-exist.md deleted file mode 100644 index 67286571f2..0000000000 --- a/.changeset/tiny-boxes-exist.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"svelte": patch ---- - -fix: update links that previously pointed to preview site diff --git a/.changeset/wild-oranges-promise.md b/.changeset/wild-oranges-promise.md deleted file mode 100644 index d79666c47e..0000000000 --- a/.changeset/wild-oranges-promise.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: properly migrate imports types prefixed with $ diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index ae56d2e619..8a98068bb1 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,23 @@ # svelte +## 5.1.4 + +### Patch Changes + +- fix: add empty stack to `CompileDiagnostic` to show error on build ([#13942](https://github.com/sveltejs/svelte/pull/13942)) + +- fix: ensure effect_tracking correctly handles tracking reactions ([#14005](https://github.com/sveltejs/svelte/pull/14005)) + +- fix: update broken links ([#13944](https://github.com/sveltejs/svelte/pull/13944)) + +- fix: more exhaustive check during `SvelteMap.set` in deriveds ([#13951](https://github.com/sveltejs/svelte/pull/13951)) + +- fix: trim whitespace while migrating blocks ([#13941](https://github.com/sveltejs/svelte/pull/13941)) + +- fix: update links that previously pointed to preview site ([#14001](https://github.com/sveltejs/svelte/pull/14001)) + +- fix: properly migrate imports types prefixed with $ ([#14007](https://github.com/sveltejs/svelte/pull/14007)) + ## 5.1.3 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 73b3e253f3..4dd788fd91 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.1.3", + "version": "5.1.4", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 1e37585b9d..6fcb1a28fc 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.1.3'; +export const VERSION = '5.1.4'; export const PUBLIC_VERSION = '5'; From b3437e60205e624d43ce9383edd725d912cbbbd2 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 29 Oct 2024 23:27:56 +0100 Subject: [PATCH 042/404] docs: note old site (#13996) --- documentation/docs/99-legacy/00-legacy-overview.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/documentation/docs/99-legacy/00-legacy-overview.md b/documentation/docs/99-legacy/00-legacy-overview.md index 0840b2256e..638a77ec1a 100644 --- a/documentation/docs/99-legacy/00-legacy-overview.md +++ b/documentation/docs/99-legacy/00-legacy-overview.md @@ -10,3 +10,5 @@ The following pages document these features for - people using Svelte 5, but with components that haven't yet been migrated Since Svelte 3/4 syntax still works in Svelte 5, we will distinguish between _legacy mode_ and _runes mode_. Once a component is in runes mode (which you can opt into by using runes, or by explicitly setting the `runes: true` compiler option), legacy mode features are no longer available. + +If you're exclusively interested in the Svelte 3/4 syntax, you can browse its documentation at [v4.svelte.dev](https://v4.svelte.dev). From 08bc37a374b452feb057fa7f0a869e0009e6cf54 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Wed, 30 Oct 2024 02:23:49 -0700 Subject: [PATCH 043/404] fix: don't access requestAnimationFrame eagerly (#14026) --- packages/svelte/src/internal/client/timing.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/svelte/src/internal/client/timing.js b/packages/svelte/src/internal/client/timing.js index dc7d2ce4ed..18dfc6c583 100644 --- a/packages/svelte/src/internal/client/timing.js +++ b/packages/svelte/src/internal/client/timing.js @@ -3,13 +3,11 @@ import { noop } from '../shared/utils.js'; import { BROWSER } from 'esm-env'; -const request_animation_frame = BROWSER ? requestAnimationFrame : noop; - const now = BROWSER ? () => performance.now() : () => Date.now(); /** @type {Raf} */ export const raf = { - tick: /** @param {any} _ */ (_) => request_animation_frame(_), + tick: /** @param {any} _ */ (_) => (BROWSER ? requestAnimationFrame : noop)(_), now: () => now(), tasks: new Set() }; From cbc2ca36ce6a5c1266daade8c1b042f79669a860 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 30 Oct 2024 11:31:21 +0100 Subject: [PATCH 044/404] fix: allow to pass in TS preference to migration (#13929) First half of https://github.com/sveltejs/kit/issues/12880 - the idea is for the migration script to check for a tsconfig.json and then set it to `true` if one is found --- .changeset/giant-wombats-allow.md | 5 +++++ packages/svelte/src/compiler/migrate/index.js | 13 ++++++++----- .../tests/migrate/samples/slot-use_ts/_config.js | 5 +++++ .../tests/migrate/samples/slot-use_ts/input.svelte | 1 + .../tests/migrate/samples/slot-use_ts/output.svelte | 9 +++++++++ packages/svelte/tests/migrate/test.ts | 4 +++- packages/svelte/types/index.d.ts | 3 ++- 7 files changed, 33 insertions(+), 7 deletions(-) create mode 100644 .changeset/giant-wombats-allow.md create mode 100644 packages/svelte/tests/migrate/samples/slot-use_ts/_config.js create mode 100644 packages/svelte/tests/migrate/samples/slot-use_ts/input.svelte create mode 100644 packages/svelte/tests/migrate/samples/slot-use_ts/output.svelte diff --git a/.changeset/giant-wombats-allow.md b/.changeset/giant-wombats-allow.md new file mode 100644 index 0000000000..e9d152a946 --- /dev/null +++ b/.changeset/giant-wombats-allow.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: allow to pass in TS preference to migration diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index 0bf3ac08b2..5630b8b34c 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -39,10 +39,10 @@ class MigrationError extends Error { * May throw an error if the code is too complex to migrate automatically. * * @param {string} source - * @param {{filename?: string}} [options] + * @param {{ filename?: string, use_ts?: boolean }} [options] * @returns {{ code: string; }} */ -export function migrate(source, { filename } = {}) { +export function migrate(source, { filename, use_ts } = {}) { let og_source = source; try { has_migration_task = false; @@ -115,9 +115,12 @@ export function migrate(source, { filename } = {}) { derived_components: new Map(), derived_labeled_statements: new Set(), has_svelte_self: false, - uses_ts: !!parsed.instance?.attributes.some( - (attr) => attr.name === 'lang' && /** @type {any} */ (attr).value[0].data === 'ts' - ) + uses_ts: + // Some people could use jsdoc but have a tsconfig.json, so double-check file for jsdoc indicators + (use_ts && !source.includes('@type {')) || + !!parsed.instance?.attributes.some( + (attr) => attr.name === 'lang' && /** @type {any} */ (attr).value[0].data === 'ts' + ) }; if (parsed.module) { diff --git a/packages/svelte/tests/migrate/samples/slot-use_ts/_config.js b/packages/svelte/tests/migrate/samples/slot-use_ts/_config.js new file mode 100644 index 0000000000..dbc620d66e --- /dev/null +++ b/packages/svelte/tests/migrate/samples/slot-use_ts/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + use_ts: true +}); diff --git a/packages/svelte/tests/migrate/samples/slot-use_ts/input.svelte b/packages/svelte/tests/migrate/samples/slot-use_ts/input.svelte new file mode 100644 index 0000000000..0385342cef --- /dev/null +++ b/packages/svelte/tests/migrate/samples/slot-use_ts/input.svelte @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/slot-use_ts/output.svelte b/packages/svelte/tests/migrate/samples/slot-use_ts/output.svelte new file mode 100644 index 0000000000..3e92f5ceb1 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/slot-use_ts/output.svelte @@ -0,0 +1,9 @@ + + +{@render children?.()} \ No newline at end of file diff --git a/packages/svelte/tests/migrate/test.ts b/packages/svelte/tests/migrate/test.ts index 048c8696a4..a4b48f2fa7 100644 --- a/packages/svelte/tests/migrate/test.ts +++ b/packages/svelte/tests/migrate/test.ts @@ -6,6 +6,7 @@ import { suite, type BaseTest } from '../suite.js'; interface ParserTest extends BaseTest { skip_filename?: boolean; + use_ts?: boolean; logs?: any[]; errors?: any[]; } @@ -32,7 +33,8 @@ const { test, run } = suite(async (config, cwd) => { } const actual = migrate(input, { - filename: config.skip_filename ? undefined : `output.svelte` + filename: config.skip_filename ? undefined : `output.svelte`, + use_ts: config.use_ts }).code; if (config.logs) { diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index ebf17db6e2..b4da4c79b8 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -1290,8 +1290,9 @@ declare module 'svelte/compiler' { * May throw an error if the code is too complex to migrate automatically. * * */ - export function migrate(source: string, { filename }?: { + export function migrate(source: string, { filename, use_ts }?: { filename?: string; + use_ts?: boolean; } | undefined): { code: string; }; From 2e9dab6a2130bb091f56e1c75d51f3176fcaa7d2 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 30 Oct 2024 11:31:59 +0100 Subject: [PATCH 045/404] fix: more robust re-subscribe detection for `fromStore` (#13995) Only count down after timeout, else we would reach 0 before our own render effect reruns, but reach 1 again when the tick callback of the prior teardown runs. That would mean we re-subcribe unnecessarily and create a memory leak because the old subscription is never cleaned up. --- .changeset/eighty-pianos-sip.md | 5 +++++ packages/svelte/src/store/index-client.js | 7 ++++-- packages/svelte/tests/store/test.ts | 27 +++++++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 .changeset/eighty-pianos-sip.md diff --git a/.changeset/eighty-pianos-sip.md b/.changeset/eighty-pianos-sip.md new file mode 100644 index 0000000000..330fa54f6d --- /dev/null +++ b/.changeset/eighty-pianos-sip.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: more robust re-subscribe detection for `fromStore` diff --git a/packages/svelte/src/store/index-client.js b/packages/svelte/src/store/index-client.js index b612124e3c..98f6cf5ac1 100644 --- a/packages/svelte/src/store/index-client.js +++ b/packages/svelte/src/store/index-client.js @@ -130,9 +130,12 @@ export function fromStore(store) { subscribers += 1; return () => { - subscribers -= 1; - tick().then(() => { + // Only count down after timeout, else we would reach 0 before our own render effect reruns, + // but reach 1 again when the tick callback of the prior teardown runs. That would mean we + // re-subcribe unnecessarily and create a memory leak because the old subscription is never cleaned up. + subscribers -= 1; + if (subscribers === 0) { unsubscribe(); } diff --git a/packages/svelte/tests/store/test.ts b/packages/svelte/tests/store/test.ts index 74ad892577..b23ea195d6 100644 --- a/packages/svelte/tests/store/test.ts +++ b/packages/svelte/tests/store/test.ts @@ -666,6 +666,33 @@ describe('fromStore', () => { teardown(); }); + it('creates state from a writable store that updates after timeout', async () => { + const wait = () => new Promise((resolve) => setTimeout(resolve, 100)); + const store = { + subscribe: (cb: any) => { + // new object each time to force updates of underlying signals, + // to test the whole thing doesn't rerun more often than it should + setTimeout(() => cb({}), 0); + return () => {}; + } + }; + + const count = fromStore(store); + const log: any[] = []; + + const teardown = effect_root(() => { + render_effect(() => { + log.push(count.current); + }); + }); + + await wait(); + + assert.deepEqual(log, [undefined, {}]); + + teardown(); + }); + it('creates state from a readable store', () => { const store = readable(0); From e487f61262c2a72760454c7bd6795291171b39df Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 30 Oct 2024 11:33:32 +0100 Subject: [PATCH 046/404] docs: mark deprecated options as such (#13989) closes #13984 --- .../docs/05-special-elements/06-svelte-options.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/documentation/docs/05-special-elements/06-svelte-options.md b/documentation/docs/05-special-elements/06-svelte-options.md index 595b1c54e8..d29042e278 100644 --- a/documentation/docs/05-special-elements/06-svelte-options.md +++ b/documentation/docs/05-special-elements/06-svelte-options.md @@ -8,14 +8,19 @@ title: The `` element provides a place to specify per-component compiler options, which are detailed in the [compiler section](svelte-compiler#compile). The possible options are: -- `immutable={true}` — you never use mutable data, so the compiler can do simple referential equality checks to determine if values have changed -- `immutable={false}` — the default. Svelte will be more conservative about whether or not mutable objects have changed -- `accessors={true}` — adds getters and setters for the component's props -- `accessors={false}` — the default - `runes={true}` — forces a component into _runes mode_ (see the [Legacy APIs](legacy-overview) section) - `runes={false}` — forces a component into _legacy mode_ -- `namespace="..."` — the namespace where this component will be used, most commonly "svg"; use the "foreign" namespace to opt out of case-insensitive attribute names and HTML-specific warnings +- `namespace="..."` — the namespace where this component will be used, can be "html" (the default), "svg" or "mathml" - `customElement={...}` — the [options](custom-elements#Component-options) to use when compiling this component as a custom element. If a string is passed, it is used as the `tag` option +- `css="injected"` — the component will inject its styles inline: During server side rendering, it's injected as a ` diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected.html b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected.html index 1f0b2b95fe..3069a50ad7 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected.html +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected.html @@ -1 +1 @@ -
bar
foo
\ No newline at end of file +
bar
foo
\ No newline at end of file diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected_head.html b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected_head.html index 806f6c2db2..6474fde317 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected_head.html +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected_head.html @@ -1,5 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected_head.html b/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected_head.html index 7281935646..9c4f8a8538 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected_head.html +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options/_expected_head.html @@ -1,5 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/packages/svelte/tests/server-side-rendering/samples/css/_expected_head.html b/packages/svelte/tests/server-side-rendering/samples/css/_expected_head.html index 7281935646..9c4f8a8538 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css/_expected_head.html +++ b/packages/svelte/tests/server-side-rendering/samples/css/_expected_head.html @@ -1,5 +1 @@ - \ No newline at end of file + \ No newline at end of file From 435a9c1cd033dc4a31af9ba007eb7f5b6e24c488 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Wed, 30 Oct 2024 11:02:18 +0000 Subject: [PATCH 048/404] fix: ensure toStore subscription correctly syncs latest value (#14015) * fix: ensure toStore subscription correctly syncs latest value * Update packages/svelte/src/store/index-client.js --------- Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --- .changeset/shaggy-frogs-fetch.md | 5 +++++ packages/svelte/src/store/index-client.js | 7 +++++-- .../samples/toStore-subscribe/_config.js | 6 ++++++ .../samples/toStore-subscribe/main.svelte | 14 ++++++++++++++ 4 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 .changeset/shaggy-frogs-fetch.md create mode 100644 packages/svelte/tests/runtime-runes/samples/toStore-subscribe/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/toStore-subscribe/main.svelte diff --git a/.changeset/shaggy-frogs-fetch.md b/.changeset/shaggy-frogs-fetch.md new file mode 100644 index 0000000000..7dc7b8b920 --- /dev/null +++ b/.changeset/shaggy-frogs-fetch.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure toStore subscription correctly syncs latest value diff --git a/packages/svelte/src/store/index-client.js b/packages/svelte/src/store/index-client.js index 98f6cf5ac1..f2f1dfc4eb 100644 --- a/packages/svelte/src/store/index-client.js +++ b/packages/svelte/src/store/index-client.js @@ -42,8 +42,11 @@ export { derived, get, readable, readonly, writable } from './shared/index.js'; * @returns {Writable | Readable} */ export function toStore(get, set) { - const store = writable(get(), (set) => { - let ran = false; + let 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(); // TODO do we need a different implementation on the server? const teardown = effect_root(() => { diff --git a/packages/svelte/tests/runtime-runes/samples/toStore-subscribe/_config.js b/packages/svelte/tests/runtime-runes/samples/toStore-subscribe/_config.js new file mode 100644 index 0000000000..471fed7e09 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/toStore-subscribe/_config.js @@ -0,0 +1,6 @@ +import { test } from '../../test'; + +export default test({ + html: `1`, + mode: ['client', 'hydrate'] +}); diff --git a/packages/svelte/tests/runtime-runes/samples/toStore-subscribe/main.svelte b/packages/svelte/tests/runtime-runes/samples/toStore-subscribe/main.svelte new file mode 100644 index 0000000000..61e0cbe5b6 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/toStore-subscribe/main.svelte @@ -0,0 +1,14 @@ + + +{$store} From f52a30347f2dc91f23127a6d2271e4f66931bada Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 30 Oct 2024 12:09:47 +0100 Subject: [PATCH 049/404] fix: ensure comma is also pruned if necessary (#14032) immediately after merging #14006 I noticed there's a bug when pruning one selector in a list of several; this fixes it no changeset because not released yet --- .../compiler/phases/3-transform/css/index.js | 12 ++++++---- .../css-injected-options-minify/_config.js | 8 +++++++ .../_expected.html | 1 + .../_expected_head.html | 1 + .../css-injected-options-minify/main.svelte | 23 +++++++++++++++++++ .../css-injected-options-nested/Nested.svelte | 5 ---- .../_expected.html | 2 +- .../_expected_head.html | 2 +- 8 files changed, 43 insertions(+), 11 deletions(-) create mode 100644 packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_config.js create mode 100644 packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected.html create mode 100644 packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected_head.html create mode 100644 packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/main.svelte 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 6f87b04850..5b40867aab 100644 --- a/packages/svelte/src/compiler/phases/3-transform/css/index.js +++ b/packages/svelte/src/compiler/phases/3-transform/css/index.js @@ -214,11 +214,15 @@ const visitors = { state.code.overwrite(i, i + 1, '*/'); } } else { - prune_start = selector.start; - - if (!state.minify) { - if (i === 0) { + if (i === 0) { + if (state.minify) { + prune_start = selector.start; + } else { state.code.prependRight(selector.start, '/* (unused) '); + } + } else { + if (state.minify) { + prune_start = last; } else { state.code.overwrite(last, selector.start, ' /* (unused) '); } diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_config.js b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_config.js new file mode 100644 index 0000000000..e1a14af601 --- /dev/null +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_config.js @@ -0,0 +1,8 @@ +import { test } from '../../test'; + +export default test({ + // test checks that minification works correctly + compileOptions: { + css: 'external' + } +}); diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected.html b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected.html new file mode 100644 index 0000000000..caa872be9b --- /dev/null +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected.html @@ -0,0 +1 @@ +
foo
\ No newline at end of file diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected_head.html b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected_head.html new file mode 100644 index 0000000000..8d6e42d979 --- /dev/null +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected_head.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/main.svelte b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/main.svelte new file mode 100644 index 0000000000..13e01788e2 --- /dev/null +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/main.svelte @@ -0,0 +1,23 @@ + + +
foo
+ + diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/Nested.svelte b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/Nested.svelte index 7b6038eef3..81243bf114 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/Nested.svelte +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/Nested.svelte @@ -6,9 +6,4 @@ .bar { color: red; } - .unused { - .also-unused { - color: green; - } - } diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected.html b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected.html index 3069a50ad7..1f0b2b95fe 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected.html +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected.html @@ -1 +1 @@ -
bar
foo
\ No newline at end of file +
bar
foo
\ No newline at end of file diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected_head.html b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected_head.html index 6474fde317..6d795670ff 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected_head.html +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-nested/_expected_head.html @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file From 1434f48f7c53897905a1758b9318450c6b356522 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 30 Oct 2024 15:33:15 +0100 Subject: [PATCH 050/404] fix: make compiler error extend from `Error` (#14036) We originally didn't extend from `Error` anymore because its fields are of no real value to us, and has problems with serialization in a worker context. Turns out this was a mistake, because various build tools rely on errors being thrown as something that extends Error, else they try to wrap it in their own error. We therefore revert that change while still trying to preserve most of the advantages of not extending `Error`, namely nuking the useless stack trace and making sure the message is enumerable. --- .changeset/wet-timers-shop.md | 5 ++++ .../templates/compile-errors.js | 23 ++++++++++++++++--- packages/svelte/src/compiler/errors.js | 22 +++++++++++++++--- .../src/compiler/utils/compile_diagnostic.js | 2 -- .../impossible-migrate-with-errors/_config.js | 2 -- packages/svelte/tests/migrate/test.ts | 2 +- 6 files changed, 45 insertions(+), 11 deletions(-) create mode 100644 .changeset/wet-timers-shop.md diff --git a/.changeset/wet-timers-shop.md b/.changeset/wet-timers-shop.md new file mode 100644 index 0000000000..55d12496e0 --- /dev/null +++ b/.changeset/wet-timers-shop.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: make compiler error extend from `Error` diff --git a/packages/svelte/scripts/process-messages/templates/compile-errors.js b/packages/svelte/scripts/process-messages/templates/compile-errors.js index a407699e1b..0dc30b2c01 100644 --- a/packages/svelte/scripts/process-messages/templates/compile-errors.js +++ b/packages/svelte/scripts/process-messages/templates/compile-errors.js @@ -2,8 +2,9 @@ import { CompileDiagnostic } from './utils/compile_diagnostic.js'; /** @typedef {{ start?: number, end?: number }} NodeLike */ -class InternalCompileError extends CompileDiagnostic { - name = 'CompileError'; +class InternalCompileError extends Error { + message = ''; // ensure this property is enumerable + #diagnostic; /** * @param {string} code @@ -11,7 +12,23 @@ class InternalCompileError extends CompileDiagnostic { * @param {[number, number] | undefined} position */ constructor(code, message, position) { - super(code, message, position); + super(message); + this.stack = ''; // avoid unnecessary noise; don't set it as a class property or it becomes enumerable + + // We want to extend from Error so that various bundler plugins properly handle it. + // But we also want to share the same object shape with that of warnings, therefore + // we create an instance of the shared class an copy over its properties. + this.#diagnostic = new CompileDiagnostic(code, message, position); + Object.assign(this, this.#diagnostic); + this.name = 'CompileError'; + } + + toString() { + return this.#diagnostic.toString(); + } + + toJSON() { + return this.#diagnostic.toJSON(); } } diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js index 299154c988..8f1dae98b1 100644 --- a/packages/svelte/src/compiler/errors.js +++ b/packages/svelte/src/compiler/errors.js @@ -3,8 +3,9 @@ import { CompileDiagnostic } from './utils/compile_diagnostic.js'; /** @typedef {{ start?: number, end?: number }} NodeLike */ -class InternalCompileError extends CompileDiagnostic { - name = 'CompileError'; +class InternalCompileError extends Error { + message = ''; // ensure this property is enumerable + #diagnostic; /** * @param {string} code @@ -12,7 +13,22 @@ class InternalCompileError extends CompileDiagnostic { * @param {[number, number] | undefined} position */ constructor(code, message, position) { - super(code, message, position); + super(message); + this.stack = ''; // avoid unnecessary noise; don't set it as a class property or it becomes enumerable + // We want to extend from Error so that various bundler plugins properly handle it. + // But we also want to share the same object shape with that of warnings, therefore + // we create an instance of the shared class an copy over its properties. + this.#diagnostic = new CompileDiagnostic(code, message, position); + Object.assign(this, this.#diagnostic); + this.name = 'CompileError'; + } + + toString() { + return this.#diagnostic.toString(); + } + + toJSON() { + return this.#diagnostic.toJSON(); } } diff --git a/packages/svelte/src/compiler/utils/compile_diagnostic.js b/packages/svelte/src/compiler/utils/compile_diagnostic.js index 374a20b8b5..db938cf2bd 100644 --- a/packages/svelte/src/compiler/utils/compile_diagnostic.js +++ b/packages/svelte/src/compiler/utils/compile_diagnostic.js @@ -51,8 +51,6 @@ function get_code_frame(source, line, column) { /** @implements {ICompileDiagnostic} */ export class CompileDiagnostic { name = 'CompileDiagnostic'; - // adding an empty stack so that vite will show the file and frame during build - stack = ''; /** * @param {string} code diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-with-errors/_config.js b/packages/svelte/tests/migrate/samples/impossible-migrate-with-errors/_config.js index 57496dcba7..9e4b7222af 100644 --- a/packages/svelte/tests/migrate/samples/impossible-migrate-with-errors/_config.js +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-with-errors/_config.js @@ -19,8 +19,6 @@ export default test({ 3: unterminated template ^`, message: 'Unexpected end of input', - name: 'CompileError', - stack: '', position: [30, 30], start: { character: 30, diff --git a/packages/svelte/tests/migrate/test.ts b/packages/svelte/tests/migrate/test.ts index a4b48f2fa7..a74bc80488 100644 --- a/packages/svelte/tests/migrate/test.ts +++ b/packages/svelte/tests/migrate/test.ts @@ -28,7 +28,7 @@ const { test, run } = suite(async (config, cwd) => { if (config.errors) { console.error = (...args) => { - errors.push(...args); + errors.push(...args.map((arg) => arg.toJSON?.() ?? arg)); }; } From 8a2c97a7b87cc6bfe15c6f16d2c960895b13e955 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 30 Oct 2024 16:33:43 +0100 Subject: [PATCH 051/404] fix: extend derived/state validation error to indirect exports (#14039) Closes #14029 --- .changeset/perfect-queens-reflect.md | 5 +++++ .../2-analyze/visitors/ExportNamedDeclaration.js | 16 ++++++++++++++++ .../export-derived-state-indirect/_config.js | 10 ++++++++++ .../export-derived-state-indirect/main.svelte.js | 5 +++++ .../samples/export-state-indirect/_config.js | 10 ++++++++++ .../samples/export-state-indirect/main.svelte.js | 15 +++++++++++++++ 6 files changed, 61 insertions(+) create mode 100644 .changeset/perfect-queens-reflect.md create mode 100644 packages/svelte/tests/compiler-errors/samples/export-derived-state-indirect/_config.js create mode 100644 packages/svelte/tests/compiler-errors/samples/export-derived-state-indirect/main.svelte.js create mode 100644 packages/svelte/tests/compiler-errors/samples/export-state-indirect/_config.js create mode 100644 packages/svelte/tests/compiler-errors/samples/export-state-indirect/main.svelte.js diff --git a/.changeset/perfect-queens-reflect.md b/.changeset/perfect-queens-reflect.md new file mode 100644 index 0000000000..32c54f94f1 --- /dev/null +++ b/.changeset/perfect-queens-reflect.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: extend derived/state validation error to indirect exports 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..a9e8a05c96 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportNamedDeclaration.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportNamedDeclaration.js @@ -57,5 +57,21 @@ export function ExportNamedDeclaration(node, context) { } } } + + if (!context.state.ast_type /* .svelte.js module */ || context.state.ast_type === 'module') { + for (const specified of node.specifiers) { + const binding = context.state.scope.get(specified.local.name); + + if (!binding) continue; + + if (binding.kind === 'derived') { + e.derived_invalid_export(node); + } + + if ((binding.kind === 'state' || binding.kind === 'raw_state') && binding.reassigned) { + e.state_invalid_export(node); + } + } + } } } diff --git a/packages/svelte/tests/compiler-errors/samples/export-derived-state-indirect/_config.js b/packages/svelte/tests/compiler-errors/samples/export-derived-state-indirect/_config.js new file mode 100644 index 0000000000..ec63999d1d --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/export-derived-state-indirect/_config.js @@ -0,0 +1,10 @@ +import { test } from '../../test'; + +export default test({ + error: { + code: 'derived_invalid_export', + message: + 'Cannot export derived state from a module. To expose the current derived value, export a function returning its value', + position: [70, 76] + } +}); diff --git a/packages/svelte/tests/compiler-errors/samples/export-derived-state-indirect/main.svelte.js b/packages/svelte/tests/compiler-errors/samples/export-derived-state-indirect/main.svelte.js new file mode 100644 index 0000000000..4a63d9481d --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/export-derived-state-indirect/main.svelte.js @@ -0,0 +1,5 @@ +let count = $state(0); + +const double = $derived(count * 2); + +export { double }; diff --git a/packages/svelte/tests/compiler-errors/samples/export-state-indirect/_config.js b/packages/svelte/tests/compiler-errors/samples/export-state-indirect/_config.js new file mode 100644 index 0000000000..025928ebd5 --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/export-state-indirect/_config.js @@ -0,0 +1,10 @@ +import { test } from '../../test'; + +export default test({ + error: { + code: 'state_invalid_export', + message: + "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", + position: [211, 220] + } +}); diff --git a/packages/svelte/tests/compiler-errors/samples/export-state-indirect/main.svelte.js b/packages/svelte/tests/compiler-errors/samples/export-state-indirect/main.svelte.js new file mode 100644 index 0000000000..c26536c1c1 --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/export-state-indirect/main.svelte.js @@ -0,0 +1,15 @@ +const object = $state({ + ok: true +}); + +let primitive = $state('nope'); + +export function update_object() { + object.ok = !object.ok; +} + +export function update_primitive() { + primitive = 'yep'; +} + +export { object, primitive }; From 217e40998a9ce56f3dc926fb4e985b4547564f40 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Wed, 30 Oct 2024 08:35:26 -0700 Subject: [PATCH 052/404] chore: add changeset and comment (#14040) for #14026 --- .changeset/small-bobcats-add.md | 5 +++++ packages/svelte/src/internal/client/timing.js | 3 +++ 2 files changed, 8 insertions(+) create mode 100644 .changeset/small-bobcats-add.md diff --git a/.changeset/small-bobcats-add.md b/.changeset/small-bobcats-add.md new file mode 100644 index 0000000000..2bd686c9c1 --- /dev/null +++ b/.changeset/small-bobcats-add.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: don't access `requestAnimationFrame` until needed to reduce need for mocks during testing diff --git a/packages/svelte/src/internal/client/timing.js b/packages/svelte/src/internal/client/timing.js index 18dfc6c583..1702798952 100644 --- a/packages/svelte/src/internal/client/timing.js +++ b/packages/svelte/src/internal/client/timing.js @@ -7,6 +7,9 @@ const now = BROWSER ? () => performance.now() : () => Date.now(); /** @type {Raf} */ export const raf = { + // don't access requestAnimationFrame eagerly outside method + // this allows basic testing of user code without JSDOM + // bunder will eval and remove ternary when the user's app is built tick: /** @param {any} _ */ (_) => (BROWSER ? requestAnimationFrame : noop)(_), now: () => now(), tasks: new Set() From 6ad824046408f1bda1137d0776cbfeda24d5117a Mon Sep 17 00:00:00 2001 From: Conduitry Date: Wed, 30 Oct 2024 11:37:18 -0400 Subject: [PATCH 053/404] docs: fix various typos in reference docs (#14011) * docs: fix note about $derived * fix some other typos * revert one change * emdash tweak --- documentation/docs/01-introduction/02-getting-started.md | 2 +- documentation/docs/01-introduction/03-svelte-files.md | 2 +- documentation/docs/02-runes/03-$derived.md | 2 +- documentation/docs/02-runes/04-$effect.md | 3 +-- documentation/docs/03-template-syntax/05-await.md | 2 +- documentation/docs/03-template-syntax/06-snippet.md | 2 +- documentation/docs/03-template-syntax/15-animate.md | 6 +----- documentation/docs/04-styling/04-nested-style-elements.md | 2 +- documentation/docs/06-runtime/01-stores.md | 2 +- documentation/docs/06-runtime/03-lifecycle-hooks.md | 2 +- 10 files changed, 10 insertions(+), 15 deletions(-) diff --git a/documentation/docs/01-introduction/02-getting-started.md b/documentation/docs/01-introduction/02-getting-started.md index 37757d961f..e035e6d6df 100644 --- a/documentation/docs/01-introduction/02-getting-started.md +++ b/documentation/docs/01-introduction/02-getting-started.md @@ -21,7 +21,7 @@ There are also plugins for [Rollup](https://github.com/sveltejs/rollup-plugin-sv ## Editor tooling -The Svelte team maintains a [VS Code extension](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode) and there are integrations with various other [editors](https://sveltesociety.dev/resources#editor-support) and tools as well. +The Svelte team maintains a [VS Code extension](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode), and there are integrations with various other [editors](https://sveltesociety.dev/resources#editor-support) and tools as well. You can also check your code from the command line using [sv check](https://github.com/sveltejs/cli). diff --git a/documentation/docs/01-introduction/03-svelte-files.md b/documentation/docs/01-introduction/03-svelte-files.md index a0c0bf5cc9..cd98652370 100644 --- a/documentation/docs/01-introduction/03-svelte-files.md +++ b/documentation/docs/01-introduction/03-svelte-files.md @@ -50,7 +50,7 @@ A ` + +{#if true} +
+{/if} + +
+ +{#each { length: 5 } as _} +
+
+
+{/each} From 224fcde821db43fb22e27c7559dbcf63803a0f91 Mon Sep 17 00:00:00 2001 From: "Ahmad S." Date: Wed, 30 Oct 2024 19:37:31 +0300 Subject: [PATCH 056/404] fix: preserve the separator between selectors when an unused selector is in between (#13954) fixes #13945 --- .changeset/eighty-icons-fold.md | 5 +++++ .../compiler/phases/3-transform/css/index.js | 16 +++++++++++---- .../unused-selector-in-between/_config.js | 20 +++++++++++++++++++ .../unused-selector-in-between/expected.css | 7 +++++++ .../unused-selector-in-between/input.svelte | 14 +++++++++++++ .../_expected.html | 2 +- .../_expected_head.html | 2 +- .../css-injected-options-minify/main.svelte | 6 ++++++ 8 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 .changeset/eighty-icons-fold.md create mode 100644 packages/svelte/tests/css/samples/unused-selector-in-between/_config.js create mode 100644 packages/svelte/tests/css/samples/unused-selector-in-between/expected.css create mode 100644 packages/svelte/tests/css/samples/unused-selector-in-between/input.svelte diff --git a/.changeset/eighty-icons-fold.md b/.changeset/eighty-icons-fold.md new file mode 100644 index 0000000000..2d8f0d57bf --- /dev/null +++ b/.changeset/eighty-icons-fold.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: preserve the separator between selectors when an unused selector is in between 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 5b40867aab..ce682edd52 100644 --- a/packages/svelte/src/compiler/phases/3-transform/css/index.js +++ b/packages/svelte/src/compiler/phases/3-transform/css/index.js @@ -196,12 +196,13 @@ const visitors = { SelectorList(node, { state, next, path }) { // Only add comments if we're not inside a complex selector that itself is unused if (!path.find((n) => n.type === 'ComplexSelector' && !n.metadata.used)) { + const children = node.children; let pruning = false; - let prune_start = node.children[0].start; + let prune_start = children[0].start; let last = prune_start; - for (let i = 0; i < node.children.length; i += 1) { - const selector = node.children[i]; + for (let i = 0; i < children.length; i += 1) { + const selector = children[i]; if (selector.metadata.used === pruning) { if (pruning) { @@ -221,10 +222,17 @@ const visitors = { state.code.prependRight(selector.start, '/* (unused) '); } } else { + // If this is not the last selector add a separator + const separator = i !== children.length - 1 ? ',' : ''; + if (state.minify) { prune_start = last; + if (separator) { + while (state.code.original[prune_start - 1] !== ',') prune_start++; + state.code.update(last, prune_start, separator); + } } else { - state.code.overwrite(last, selector.start, ' /* (unused) '); + state.code.overwrite(last, selector.start, `${separator} /* (unused) `); } } } diff --git a/packages/svelte/tests/css/samples/unused-selector-in-between/_config.js b/packages/svelte/tests/css/samples/unused-selector-in-between/_config.js new file mode 100644 index 0000000000..43747eb0c1 --- /dev/null +++ b/packages/svelte/tests/css/samples/unused-selector-in-between/_config.js @@ -0,0 +1,20 @@ +import { test } from '../../test'; + +export default test({ + warnings: [ + { + code: 'css_unused_selector', + end: { + character: 72, + column: 3, + line: 10 + }, + message: 'Unused CSS selector "h4"', + start: { + character: 70, + column: 1, + line: 10 + } + } + ] +}); diff --git a/packages/svelte/tests/css/samples/unused-selector-in-between/expected.css b/packages/svelte/tests/css/samples/unused-selector-in-between/expected.css new file mode 100644 index 0000000000..5a93aa53b7 --- /dev/null +++ b/packages/svelte/tests/css/samples/unused-selector-in-between/expected.css @@ -0,0 +1,7 @@ + + h1.svelte-xyz, + h2.svelte-xyz, + h3.svelte-xyz, /* (unused) h4*/ + p.svelte-xyz { + color: red; + } diff --git a/packages/svelte/tests/css/samples/unused-selector-in-between/input.svelte b/packages/svelte/tests/css/samples/unused-selector-in-between/input.svelte new file mode 100644 index 0000000000..c1e149ba76 --- /dev/null +++ b/packages/svelte/tests/css/samples/unused-selector-in-between/input.svelte @@ -0,0 +1,14 @@ +

h1

+

h2

+

h3

+

p

+ + diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected.html b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected.html index caa872be9b..60e974bd1a 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected.html +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected.html @@ -1 +1 @@ -
foo
\ No newline at end of file +
foo
\ No newline at end of file diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected_head.html b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected_head.html index 8d6e42d979..5350e77a49 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected_head.html +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/_expected_head.html @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/main.svelte b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/main.svelte index 13e01788e2..6d9ecba8df 100644 --- a/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/main.svelte +++ b/packages/svelte/tests/server-side-rendering/samples/css-injected-options-minify/main.svelte @@ -20,4 +20,10 @@ .foo, .unused { color: green; } + .unused, .foo { + color: green; + } + .foo, .unused, .foo { + color: green; + } From 4157db94c439148109c8bc407365371f7babae7e Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 30 Oct 2024 12:50:33 -0400 Subject: [PATCH 057/404] fix: correct typo in compiler error messages (#14044) --- .changeset/beige-stingrays-sip.md | 5 +++++ .../docs/98-reference/.generated/compile-errors.md | 2 +- packages/svelte/messages/compile-errors/template.md | 2 +- packages/svelte/src/compiler/errors.js | 4 ++-- .../src/compiler/phases/3-transform/css/index.js | 10 +++++----- .../samples/catch-without-await/_config.js | 2 +- .../samples/else-before-closing-3/_config.js | 2 +- .../samples/else-before-closing/_config.js | 2 +- .../samples/else-if-before-closing-2/_config.js | 2 +- .../samples/then-before-closing/_config.js | 2 +- .../samples/then-without-await/_config.js | 2 +- .../tests/migrate/samples/derivations/input.svelte | 2 +- .../tests/migrate/samples/derivations/output.svelte | 4 ++-- 13 files changed, 23 insertions(+), 18 deletions(-) create mode 100644 .changeset/beige-stingrays-sip.md diff --git a/.changeset/beige-stingrays-sip.md b/.changeset/beige-stingrays-sip.md new file mode 100644 index 0000000000..408d91d403 --- /dev/null +++ b/.changeset/beige-stingrays-sip.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: replace typo in compiler error messages diff --git a/documentation/docs/98-reference/.generated/compile-errors.md b/documentation/docs/98-reference/.generated/compile-errors.md index 1b8511a6f6..3981822d84 100644 --- a/documentation/docs/98-reference/.generated/compile-errors.md +++ b/documentation/docs/98-reference/.generated/compile-errors.md @@ -119,7 +119,7 @@ Can only bind to state or props ### block_invalid_continuation_placement ``` -{:...} block is invalid at this position (did you forget to close the preceeding element or block?) +{:...} block is invalid at this position (did you forget to close the preceding element or block?) ``` ### block_invalid_elseif diff --git a/packages/svelte/messages/compile-errors/template.md b/packages/svelte/messages/compile-errors/template.md index d606e4fd84..cddca87d9c 100644 --- a/packages/svelte/messages/compile-errors/template.md +++ b/packages/svelte/messages/compile-errors/template.md @@ -74,7 +74,7 @@ ## block_invalid_continuation_placement -> {:...} block is invalid at this position (did you forget to close the preceeding element or block?) +> {:...} block is invalid at this position (did you forget to close the preceding element or block?) ## block_invalid_elseif diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js index 8f1dae98b1..00e2246ccf 100644 --- a/packages/svelte/src/compiler/errors.js +++ b/packages/svelte/src/compiler/errors.js @@ -747,12 +747,12 @@ export function block_duplicate_clause(node, name) { } /** - * {:...} block is invalid at this position (did you forget to close the preceeding element or block?) + * {:...} block is invalid at this position (did you forget to close the preceding element or block?) * @param {null | number | NodeLike} node * @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 preceeding element or block?)"); + e(node, "block_invalid_continuation_placement", "{:...} block is invalid at this position (did you forget to close the preceding element or block?)"); } /** 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 ce682edd52..62c520e994 100644 --- a/packages/svelte/src/compiler/phases/3-transform/css/index.js +++ b/packages/svelte/src/compiler/phases/3-transform/css/index.js @@ -48,7 +48,7 @@ export function render_stylesheet(source, analysis, options) { code.remove(0, ast.content.start); code.remove(/** @type {number} */ (ast.content.end), source.length); if (state.minify) { - remove_preceeding_whitespace(ast.content.end, state); + remove_preceding_whitespace(ast.content.end, state); } const css = { @@ -122,7 +122,7 @@ const visitors = { index++; } } else if (state.minify) { - remove_preceeding_whitespace(node.start, state); + remove_preceding_whitespace(node.start, state); // Don't minify whitespace in custom properties, since some browsers (Chromium < 99) // treat --foo: ; and --foo:; differently @@ -136,8 +136,8 @@ const visitors = { }, Rule(node, { state, next, visit }) { if (state.minify) { - remove_preceeding_whitespace(node.start, state); - remove_preceeding_whitespace(node.block.end - 1, state); + remove_preceding_whitespace(node.start, state); + remove_preceding_whitespace(node.block.end - 1, state); } // keep empty rules in dev, because it's convenient to @@ -385,7 +385,7 @@ const visitors = { * @param {number} end * @param {State} state */ -function remove_preceeding_whitespace(end, state) { +function remove_preceding_whitespace(end, state) { let start = end; while (/\s/.test(state.code.original[start - 1])) start--; if (start < end) state.code.remove(start, end); diff --git a/packages/svelte/tests/compiler-errors/samples/catch-without-await/_config.js b/packages/svelte/tests/compiler-errors/samples/catch-without-await/_config.js index eaca30bd34..b8fca63f8c 100644 --- a/packages/svelte/tests/compiler-errors/samples/catch-without-await/_config.js +++ b/packages/svelte/tests/compiler-errors/samples/catch-without-await/_config.js @@ -4,7 +4,7 @@ export default test({ error: { code: 'block_invalid_continuation_placement', message: - '{:...} block is invalid at this position (did you forget to close the preceeding element or block?)', + '{:...} block is invalid at this position (did you forget to close the preceding element or block?)', position: [1, 1] } }); diff --git a/packages/svelte/tests/compiler-errors/samples/else-before-closing-3/_config.js b/packages/svelte/tests/compiler-errors/samples/else-before-closing-3/_config.js index daf005c900..050f58aa96 100644 --- a/packages/svelte/tests/compiler-errors/samples/else-before-closing-3/_config.js +++ b/packages/svelte/tests/compiler-errors/samples/else-before-closing-3/_config.js @@ -4,7 +4,7 @@ export default test({ error: { code: 'block_invalid_continuation_placement', message: - '{:...} block is invalid at this position (did you forget to close the preceeding element or block?)', + '{:...} block is invalid at this position (did you forget to close the preceding element or block?)', position: [6, 6] } }); diff --git a/packages/svelte/tests/compiler-errors/samples/else-before-closing/_config.js b/packages/svelte/tests/compiler-errors/samples/else-before-closing/_config.js index 5053be23bd..4e66c92c65 100644 --- a/packages/svelte/tests/compiler-errors/samples/else-before-closing/_config.js +++ b/packages/svelte/tests/compiler-errors/samples/else-before-closing/_config.js @@ -4,7 +4,7 @@ export default test({ error: { code: 'block_invalid_continuation_placement', message: - '{:...} block is invalid at this position (did you forget to close the preceeding element or block?)', + '{:...} block is invalid at this position (did you forget to close the preceding element or block?)', position: [18, 18] } }); diff --git a/packages/svelte/tests/compiler-errors/samples/else-if-before-closing-2/_config.js b/packages/svelte/tests/compiler-errors/samples/else-if-before-closing-2/_config.js index 43839b6723..4a440af93e 100644 --- a/packages/svelte/tests/compiler-errors/samples/else-if-before-closing-2/_config.js +++ b/packages/svelte/tests/compiler-errors/samples/else-if-before-closing-2/_config.js @@ -4,7 +4,7 @@ export default test({ error: { code: 'block_invalid_continuation_placement', message: - '{:...} block is invalid at this position (did you forget to close the preceeding element or block?)', + '{:...} block is invalid at this position (did you forget to close the preceding element or block?)', position: [17, 17] } }); diff --git a/packages/svelte/tests/compiler-errors/samples/then-before-closing/_config.js b/packages/svelte/tests/compiler-errors/samples/then-before-closing/_config.js index 23c71262d2..03a5996737 100644 --- a/packages/svelte/tests/compiler-errors/samples/then-before-closing/_config.js +++ b/packages/svelte/tests/compiler-errors/samples/then-before-closing/_config.js @@ -4,7 +4,7 @@ export default test({ error: { code: 'block_invalid_continuation_placement', message: - '{:...} block is invalid at this position (did you forget to close the preceeding element or block?)', + '{:...} block is invalid at this position (did you forget to close the preceding element or block?)', position: [21, 21] } }); diff --git a/packages/svelte/tests/compiler-errors/samples/then-without-await/_config.js b/packages/svelte/tests/compiler-errors/samples/then-without-await/_config.js index eaca30bd34..b8fca63f8c 100644 --- a/packages/svelte/tests/compiler-errors/samples/then-without-await/_config.js +++ b/packages/svelte/tests/compiler-errors/samples/then-without-await/_config.js @@ -4,7 +4,7 @@ export default test({ error: { code: 'block_invalid_continuation_placement', message: - '{:...} block is invalid at this position (did you forget to close the preceeding element or block?)', + '{:...} block is invalid at this position (did you forget to close the preceding element or block?)', position: [1, 1] } }); diff --git a/packages/svelte/tests/migrate/samples/derivations/input.svelte b/packages/svelte/tests/migrate/samples/derivations/input.svelte index 8f89a59385..a120bbe7ee 100644 --- a/packages/svelte/tests/migrate/samples/derivations/input.svelte +++ b/packages/svelte/tests/migrate/samples/derivations/input.svelte @@ -6,7 +6,7 @@ // no semicolon at the end $: time_8 = count * 8 $: ({ time_16 } = { time_16: count * 16 }) - // preceeding let that doesn't do anything + // preceding let that doesn't do anything let time_32; $: time_32 = count * doubled; let very_high; diff --git a/packages/svelte/tests/migrate/samples/derivations/output.svelte b/packages/svelte/tests/migrate/samples/derivations/output.svelte index 23bac4ccaf..79ff089018 100644 --- a/packages/svelte/tests/migrate/samples/derivations/output.svelte +++ b/packages/svelte/tests/migrate/samples/derivations/output.svelte @@ -6,11 +6,11 @@ // no semicolon at the end let time_8 = $derived(count * 8) let { time_16 } = $derived({ time_16: count * 16 }) - // preceeding let that doesn't do anything + // preceding let that doesn't do anything let time_32 = $derived(count * doubled); let very_high = $derived(time_32 * count); -{count} / {doubled} / {quadrupled} / {time_8} / {time_16} \ No newline at end of file +{count} / {doubled} / {quadrupled} / {time_8} / {time_16} From f519b3d7b75b879842d65bcd67928dd528a895e1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2024 09:52:55 -0700 Subject: [PATCH 058/404] Version Packages (#14031) Co-authored-by: github-actions[bot] --- .changeset/beige-stingrays-sip.md | 5 ----- .changeset/eighty-icons-fold.md | 5 ----- .changeset/eighty-pianos-sip.md | 5 ----- .changeset/giant-wombats-allow.md | 5 ----- .changeset/perfect-queens-reflect.md | 5 ----- .changeset/red-berries-watch.md | 5 ----- .changeset/shaggy-frogs-fetch.md | 5 ----- .changeset/small-bobcats-add.md | 5 ----- .changeset/thin-icons-sort.md | 5 ----- .changeset/wet-timers-shop.md | 5 ----- packages/svelte/CHANGELOG.md | 24 ++++++++++++++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 13 files changed, 26 insertions(+), 52 deletions(-) delete mode 100644 .changeset/beige-stingrays-sip.md delete mode 100644 .changeset/eighty-icons-fold.md delete mode 100644 .changeset/eighty-pianos-sip.md delete mode 100644 .changeset/giant-wombats-allow.md delete mode 100644 .changeset/perfect-queens-reflect.md delete mode 100644 .changeset/red-berries-watch.md delete mode 100644 .changeset/shaggy-frogs-fetch.md delete mode 100644 .changeset/small-bobcats-add.md delete mode 100644 .changeset/thin-icons-sort.md delete mode 100644 .changeset/wet-timers-shop.md diff --git a/.changeset/beige-stingrays-sip.md b/.changeset/beige-stingrays-sip.md deleted file mode 100644 index 408d91d403..0000000000 --- a/.changeset/beige-stingrays-sip.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: replace typo in compiler error messages diff --git a/.changeset/eighty-icons-fold.md b/.changeset/eighty-icons-fold.md deleted file mode 100644 index 2d8f0d57bf..0000000000 --- a/.changeset/eighty-icons-fold.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: preserve the separator between selectors when an unused selector is in between diff --git a/.changeset/eighty-pianos-sip.md b/.changeset/eighty-pianos-sip.md deleted file mode 100644 index 330fa54f6d..0000000000 --- a/.changeset/eighty-pianos-sip.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: more robust re-subscribe detection for `fromStore` diff --git a/.changeset/giant-wombats-allow.md b/.changeset/giant-wombats-allow.md deleted file mode 100644 index e9d152a946..0000000000 --- a/.changeset/giant-wombats-allow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: allow to pass in TS preference to migration diff --git a/.changeset/perfect-queens-reflect.md b/.changeset/perfect-queens-reflect.md deleted file mode 100644 index 32c54f94f1..0000000000 --- a/.changeset/perfect-queens-reflect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: extend derived/state validation error to indirect exports diff --git a/.changeset/red-berries-watch.md b/.changeset/red-berries-watch.md deleted file mode 100644 index 92460c9cd0..0000000000 --- a/.changeset/red-berries-watch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: minify inject CSS in prod mode diff --git a/.changeset/shaggy-frogs-fetch.md b/.changeset/shaggy-frogs-fetch.md deleted file mode 100644 index 7dc7b8b920..0000000000 --- a/.changeset/shaggy-frogs-fetch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure toStore subscription correctly syncs latest value diff --git a/.changeset/small-bobcats-add.md b/.changeset/small-bobcats-add.md deleted file mode 100644 index 2bd686c9c1..0000000000 --- a/.changeset/small-bobcats-add.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: don't access `requestAnimationFrame` until needed to reduce need for mocks during testing diff --git a/.changeset/thin-icons-sort.md b/.changeset/thin-icons-sort.md deleted file mode 100644 index 92dc82a825..0000000000 --- a/.changeset/thin-icons-sort.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure element effects are executed in the correct order diff --git a/.changeset/wet-timers-shop.md b/.changeset/wet-timers-shop.md deleted file mode 100644 index 55d12496e0..0000000000 --- a/.changeset/wet-timers-shop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: make compiler error extend from `Error` diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 8a98068bb1..ceb63fece4 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,29 @@ # svelte +## 5.1.5 + +### Patch Changes + +- fix: replace typo in compiler error messages ([#14044](https://github.com/sveltejs/svelte/pull/14044)) + +- fix: preserve the separator between selectors when an unused selector is in between ([#13954](https://github.com/sveltejs/svelte/pull/13954)) + +- fix: more robust re-subscribe detection for `fromStore` ([#13995](https://github.com/sveltejs/svelte/pull/13995)) + +- fix: allow to pass in TS preference to migration ([#13929](https://github.com/sveltejs/svelte/pull/13929)) + +- fix: extend derived/state validation error to indirect exports ([#14039](https://github.com/sveltejs/svelte/pull/14039)) + +- fix: minify inject CSS in prod mode ([#14006](https://github.com/sveltejs/svelte/pull/14006)) + +- fix: ensure toStore subscription correctly syncs latest value ([#14015](https://github.com/sveltejs/svelte/pull/14015)) + +- fix: don't access `requestAnimationFrame` until needed to reduce need for mocks during testing ([#14040](https://github.com/sveltejs/svelte/pull/14040)) + +- fix: ensure element effects are executed in the correct order ([#14038](https://github.com/sveltejs/svelte/pull/14038)) + +- fix: make compiler error extend from `Error` ([#14036](https://github.com/sveltejs/svelte/pull/14036)) + ## 5.1.4 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 4dd788fd91..bd610bcd69 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.1.4", + "version": "5.1.5", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 6fcb1a28fc..11711b5dc2 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.1.4'; +export const VERSION = '5.1.5'; export const PUBLIC_VERSION = '5'; From bce1c4898c1460926662f47aeff1588681720806 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Wed, 30 Oct 2024 19:35:06 +0000 Subject: [PATCH 059/404] fix: ensure child effects are destroyed before their deriveds (#14043) * fix: ensure legacy props cache last value when destroyed * fix runes * better approach * better approach * kill code * lint --- .changeset/khaki-carrots-mix.md | 5 +++++ .../src/internal/client/reactivity/effects.js | 2 +- .../src/internal/client/reactivity/props.js | 12 +++--------- packages/svelte/src/internal/client/runtime.js | 2 +- .../samples/props-reactive-destroy/Child.svelte | 11 +++++++++++ .../samples/props-reactive-destroy/_config.js | 10 ++++++++++ .../samples/props-reactive-destroy/main.svelte | 17 +++++++++++++++++ .../samples/props-reactive-destroy/Child.svelte | 11 +++++++++++ .../samples/props-reactive-destroy/_config.js | 10 ++++++++++ .../samples/props-reactive-destroy/main.svelte | 17 +++++++++++++++++ 10 files changed, 86 insertions(+), 11 deletions(-) create mode 100644 .changeset/khaki-carrots-mix.md create mode 100644 packages/svelte/tests/runtime-legacy/samples/props-reactive-destroy/Child.svelte create mode 100644 packages/svelte/tests/runtime-legacy/samples/props-reactive-destroy/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/props-reactive-destroy/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/props-reactive-destroy/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/props-reactive-destroy/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/props-reactive-destroy/main.svelte diff --git a/.changeset/khaki-carrots-mix.md b/.changeset/khaki-carrots-mix.md new file mode 100644 index 0000000000..f5ef6b6418 --- /dev/null +++ b/.changeset/khaki-carrots-mix.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure child effects are destroyed before their deriveds diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 82baeae288..daf28eeec2 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -437,8 +437,8 @@ export function destroy_effect(effect, remove_dom = true) { removed = true; } - destroy_effect_deriveds(effect); 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/props.js b/packages/svelte/src/internal/client/reactivity/props.js index eec8e70dc6..e24deb5a20 100644 --- a/packages/svelte/src/internal/client/reactivity/props.js +++ b/packages/svelte/src/internal/client/reactivity/props.js @@ -1,4 +1,4 @@ -/** @import { Derived, Source } from './types.js' */ +/** @import { Source } from './types.js' */ import { DEV } from 'esm-env'; import { PROPS_IS_BINDABLE, @@ -12,7 +12,6 @@ import { mutable_source, set, source } from './sources.js'; import { derived, derived_safe_equal } from './deriveds.js'; import { active_effect, - active_reaction, get, is_signals_recorded, set_active_effect, @@ -21,7 +20,7 @@ import { } from '../runtime.js'; import { safe_equals } from './equality.js'; import * as e from '../errors.js'; -import { BRANCH_EFFECT, DESTROYED, LEGACY_DERIVED_PROP, ROOT_EFFECT } from '../constants.js'; +import { BRANCH_EFFECT, LEGACY_DERIVED_PROP, ROOT_EFFECT } from '../constants.js'; import { proxy } from '../proxy.js'; import { capture_store_binding } from './store.js'; @@ -369,17 +368,12 @@ 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); - var current_derived = /** @type {Derived} */ (active_reaction); - // If the getter from the parent returns undefined, switch - // to using the local value from inner_current_value instead, - // as the parent value might have been torn down - if (from_child || (parent_value === undefined && (current_derived.f & DESTROYED) !== 0)) { + if (from_child) { from_child = false; was_from_child = true; return child_value; diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 26c8d9adbb..6bed3825c7 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -432,12 +432,12 @@ export function update_effect(effect) { } try { - destroy_effect_deriveds(effect); if ((flags & BLOCK_EFFECT) !== 0) { destroy_block_effect_children(effect); } else { destroy_effect_children(effect); } + destroy_effect_deriveds(effect); execute_effect_teardown(effect); var teardown = update_reaction(effect); diff --git a/packages/svelte/tests/runtime-legacy/samples/props-reactive-destroy/Child.svelte b/packages/svelte/tests/runtime-legacy/samples/props-reactive-destroy/Child.svelte new file mode 100644 index 0000000000..dc6326f8c4 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/props-reactive-destroy/Child.svelte @@ -0,0 +1,11 @@ + + +{data ? '' : null} \ No newline at end of file diff --git a/packages/svelte/tests/runtime-legacy/samples/props-reactive-destroy/_config.js b/packages/svelte/tests/runtime-legacy/samples/props-reactive-destroy/_config.js new file mode 100644 index 0000000000..363c850c8b --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/props-reactive-destroy/_config.js @@ -0,0 +1,10 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + test({ assert, logs, target }) { + target.querySelector('button')?.click(); + flushSync(); + assert.deepEqual(logs, ['should fire once']); + } +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/props-reactive-destroy/main.svelte b/packages/svelte/tests/runtime-legacy/samples/props-reactive-destroy/main.svelte new file mode 100644 index 0000000000..00c6a5f71c --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/props-reactive-destroy/main.svelte @@ -0,0 +1,17 @@ + + + + +{#if active} + +{/if} \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/props-reactive-destroy/Child.svelte b/packages/svelte/tests/runtime-runes/samples/props-reactive-destroy/Child.svelte new file mode 100644 index 0000000000..d16689b07d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/props-reactive-destroy/Child.svelte @@ -0,0 +1,11 @@ + + +{data ? '' : null} diff --git a/packages/svelte/tests/runtime-runes/samples/props-reactive-destroy/_config.js b/packages/svelte/tests/runtime-runes/samples/props-reactive-destroy/_config.js new file mode 100644 index 0000000000..363c850c8b --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/props-reactive-destroy/_config.js @@ -0,0 +1,10 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + test({ assert, logs, target }) { + target.querySelector('button')?.click(); + flushSync(); + assert.deepEqual(logs, ['should fire once']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/props-reactive-destroy/main.svelte b/packages/svelte/tests/runtime-runes/samples/props-reactive-destroy/main.svelte new file mode 100644 index 0000000000..604237072a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/props-reactive-destroy/main.svelte @@ -0,0 +1,17 @@ + + + + +{#if active} + +{/if} From e78ea1d555732834983bbf2b48b7fbb78eb3c86c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2024 19:39:01 +0000 Subject: [PATCH 060/404] Version Packages (#14047) Co-authored-by: github-actions[bot] --- .changeset/khaki-carrots-mix.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/khaki-carrots-mix.md diff --git a/.changeset/khaki-carrots-mix.md b/.changeset/khaki-carrots-mix.md deleted file mode 100644 index f5ef6b6418..0000000000 --- a/.changeset/khaki-carrots-mix.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure child effects are destroyed before their deriveds diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index ceb63fece4..1a25f6dc4a 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.1.6 + +### Patch Changes + +- fix: ensure child effects are destroyed before their deriveds ([#14043](https://github.com/sveltejs/svelte/pull/14043)) + ## 5.1.5 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index bd610bcd69..3612e4e67a 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.1.5", + "version": "5.1.6", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 11711b5dc2..de8344ad66 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.1.5'; +export const VERSION = '5.1.6'; export const PUBLIC_VERSION = '5'; From 4c6255f8ddd9252a5ef245667e0b379528c60938 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 30 Oct 2024 15:39:38 -0400 Subject: [PATCH 061/404] chore: remove unnecessary ternary (#14045) --- .../phases/3-transform/client/visitors/BindDirective.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) 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 d24c1769d0..79969240c7 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 @@ -242,13 +242,11 @@ export function BindDirective(node, context) { if (node.name === 'this') { context.state.init.push(b.stmt(call)); } else { - const has_action_directive = + const has_use = parent.type === 'RegularElement' && parent.attributes.find((a) => a.type === 'UseDirective'); - if (has_action_directive) { - context.state.init.push( - b.stmt(has_action_directive ? b.call('$.effect', b.thunk(call)) : call) - ); + if (has_use) { + context.state.init.push(b.stmt(b.call('$.effect', b.thunk(call)))); } else { context.state.after_update.push(b.stmt(call)); } From cdec39afac9acf5293eda5bc9208cd00300b47c2 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Wed, 30 Oct 2024 21:07:38 +0000 Subject: [PATCH 062/404] fix: ensure custom element attribute/prop changes are in their own context (#14016) Fixes #13848. When we set custom element attributes/props, we should be doing so without the current effect/reaction active. Otherwise, the custom element lifecycle might attach effects/dependencies to the wrong reaction and all manner of things can incorrectly occur --------- Co-authored-by: Simon Holthausen --- .changeset/wild-parents-begin.md | 5 +++ .../client/dom/elements/attributes.js | 28 ++++++++++++++--- .../element-effect-context/_config.js | 24 ++++++++++++++ .../element-effect-context/main.svelte | 31 +++++++++++++++++++ 4 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 .changeset/wild-parents-begin.md create mode 100644 packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/_config.js create mode 100644 packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/main.svelte diff --git a/.changeset/wild-parents-begin.md b/.changeset/wild-parents-begin.md new file mode 100644 index 0000000000..ac98fa0e3e --- /dev/null +++ b/.changeset/wild-parents-begin.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure custom element attribute/prop changes are in their own context diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index 30c7712f1e..545cab20f8 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -7,6 +7,12 @@ import * as w from '../../warnings.js'; import { LOADING_ATTR_SYMBOL } from '../../constants.js'; import { queue_idle_task, queue_micro_task } from '../task.js'; import { is_capture_event, is_delegated, normalize_attribute } from '../../../../utils.js'; +import { + active_effect, + active_reaction, + set_active_effect, + set_active_reaction +} from '../../runtime.js'; /** * The value/checked attribute in the template actually corresponds to the defaultValue property, so we need @@ -145,10 +151,24 @@ export function set_xlink_attribute(dom, attribute, value) { * @param {any} value */ export function set_custom_element_data(node, prop, value) { - if (get_setters(node).includes(prop)) { - node[prop] = value; - } else { - set_attribute(node, prop, value); + // We need to ensure that setting custom element props, which can + // invoke lifecycle methods on other custom elements, does not also + // associate those lifecycle methods with the current active reaction + // or effect + var previous_reaction = active_reaction; + var previous_effect = active_effect; + + set_active_reaction(null); + set_active_effect(null); + try { + if (get_setters(node).includes(prop)) { + node[prop] = value; + } else { + set_attribute(node, prop, value); + } + } finally { + set_active_reaction(previous_reaction); + set_active_effect(previous_effect); } } 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 new file mode 100644 index 0000000000..3cf7a66df1 --- /dev/null +++ b/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/_config.js @@ -0,0 +1,24 @@ +import { test } from '../../assert'; +const tick = () => Promise.resolve(); + +export default test({ + async test({ assert, target }) { + target.innerHTML = ''; + await tick(); + await tick(); + + /** @type {any} */ + const el = target.querySelector('my-app'); + const button = el.shadowRoot.querySelector('button'); + const p = el.shadowRoot.querySelector('my-tracking').shadowRoot.querySelector('p'); + + assert.equal(button.innerHTML, '0'); + assert.equal(p.innerHTML, 'false'); + + button.click(); + await tick(); + + assert.equal(button.innerHTML, '1'); + assert.equal(p.innerHTML, 'false'); + } +}); diff --git a/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/main.svelte b/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/main.svelte new file mode 100644 index 0000000000..fa35c8a086 --- /dev/null +++ b/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/main.svelte @@ -0,0 +1,31 @@ + + + + + + + + From 4a5a0b15d90e7c735dab2bc6b434c7feae204cc6 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Wed, 30 Oct 2024 21:08:33 +0000 Subject: [PATCH 063/404] fix: ensure onMount correctly fires when new expressions are used (#14049) * fix: ensure onMount correctly fires when used externally * fix root context * fix root context * revert * alternative fix --- .changeset/early-hounds-float.md | 5 +++++ .../phases/2-analyze/visitors/NewExpression.js | 2 ++ .../samples/onmount-external/_config.js | 7 +++++++ .../samples/onmount-external/main.svelte | 13 +++++++++++++ 4 files changed, 27 insertions(+) create mode 100644 .changeset/early-hounds-float.md create mode 100644 packages/svelte/tests/runtime-legacy/samples/onmount-external/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/onmount-external/main.svelte diff --git a/.changeset/early-hounds-float.md b/.changeset/early-hounds-float.md new file mode 100644 index 0000000000..0a708c59e4 --- /dev/null +++ b/.changeset/early-hounds-float.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure onMount correctly fires when new expressions are used diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/NewExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/NewExpression.js index 6b393ae9b9..bcb2400932 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/NewExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/NewExpression.js @@ -11,5 +11,7 @@ export function NewExpression(node, context) { w.perf_avoid_inline_class(node); } + context.state.analysis.needs_context = true; + context.next(); } diff --git a/packages/svelte/tests/runtime-legacy/samples/onmount-external/_config.js b/packages/svelte/tests/runtime-legacy/samples/onmount-external/_config.js new file mode 100644 index 0000000000..6cf976bd48 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/onmount-external/_config.js @@ -0,0 +1,7 @@ +import { test } from '../../test'; + +export default test({ + async test({ assert, logs }) { + assert.deepEqual(logs, ['mounted']); + } +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/onmount-external/main.svelte b/packages/svelte/tests/runtime-legacy/samples/onmount-external/main.svelte new file mode 100644 index 0000000000..e2e7dad8dc --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/onmount-external/main.svelte @@ -0,0 +1,13 @@ + + + From dc3c82cb2d11fc320a2f30c2c6de9266907ae4e1 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 30 Oct 2024 18:39:48 -0400 Subject: [PATCH 064/404] chore: add sync request workflow (#14055) --- .github/workflows/sync-request.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/sync-request.yml diff --git a/.github/workflows/sync-request.yml b/.github/workflows/sync-request.yml new file mode 100644 index 0000000000..a4147faf73 --- /dev/null +++ b/.github/workflows/sync-request.yml @@ -0,0 +1,21 @@ +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 ee2f4a209bdcfff7760843353567c99831d2e2ac Mon Sep 17 00:00:00 2001 From: Dave Snider Date: Wed, 30 Oct 2024 23:45:08 +0100 Subject: [PATCH 065/404] docs: Update in and out docs to show the imports (#14050) * Update in and out docs to show the imports * Apply suggestions from code review --------- Co-authored-by: Rich Harris --- .../docs/03-template-syntax/14-in-and-out.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/documentation/docs/03-template-syntax/14-in-and-out.md b/documentation/docs/03-template-syntax/14-in-and-out.md index feb5aa84c5..865398b46e 100644 --- a/documentation/docs/03-template-syntax/14-in-and-out.md +++ b/documentation/docs/03-template-syntax/14-in-and-out.md @@ -5,6 +5,17 @@ title: in: and out: The `in:` and `out:` directives are identical to [`transition:`](transition), except that the resulting transitions are not bidirectional — an `in` transition will continue to 'play' alongside the `out` transition, rather than reversing, if the block is outroed while the transition is in progress. If an out transition is aborted, transitions will restart from scratch. ```svelte + + + + {#if visible}
flies in, fades out
{/if} From 0efe031edc61e01e175caf0f8b9cb259c05e6ce8 Mon Sep 17 00:00:00 2001 From: gubo97000 Date: Thu, 31 Oct 2024 01:25:28 +0200 Subject: [PATCH 066/404] Update 07-$inspect.md (#14048) moved note to top --- documentation/docs/02-runes/07-$inspect.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/docs/02-runes/07-$inspect.md b/documentation/docs/02-runes/07-$inspect.md index 07429a2818..1afe25deca 100644 --- a/documentation/docs/02-runes/07-$inspect.md +++ b/documentation/docs/02-runes/07-$inspect.md @@ -2,6 +2,8 @@ title: $inspect --- +> [!NOTE] `$inspect` only works during development. In a production build it becomes a noop. + The `$inspect` rune is roughly equivalent to `console.log`, with the exception that it will re-run whenever its argument changes. `$inspect` tracks reactive state deeply, meaning that updating something inside an object or array using fine-grained reactivity will cause it to re-fire ([demo](/playground/untitled#H4sIAAAAAAAACkWQ0YqDQAxFfyUMhSotdZ-tCvu431AXtGOqQ2NmmMm0LOK_r7Utfby5JzeXTOpiCIPKT5PidkSVq2_n1F7Jn3uIcEMSXHSw0evHpAjaGydVzbUQCmgbWaCETZBWMPlKj29nxBDaHj_edkAiu12JhdkYDg61JGvE_s2nR8gyuBuiJZuDJTyQ7eE-IEOzog1YD80Lb0APLfdYc5F9qnFxjiKWwbImo6_llKRQVs-2u91c_bD2OCJLkT3JZasw7KLA2XCX31qKWE6vIzNk1fKE0XbmYrBTufiI8-_8D2cUWBA_AQAA)): ```svelte @@ -40,5 +42,3 @@ A convenient way to find the origin of some change is to pass `console.trace` to // @errors: 2304 $inspect(stuff).with(console.trace); ``` - -> [!NOTE] `$inspect` only works during development. In a production build it becomes a noop. From 6b6445a9349750699f1953f1519b08e774e168df Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 30 Oct 2024 19:55:04 -0400 Subject: [PATCH 067/404] add parameters to `in:fly` (#14057) Without a `y` value, `fly` is indistinguishable from `fade` --- documentation/docs/03-template-syntax/14-in-and-out.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/03-template-syntax/14-in-and-out.md b/documentation/docs/03-template-syntax/14-in-and-out.md index 865398b46e..f4e37c845e 100644 --- a/documentation/docs/03-template-syntax/14-in-and-out.md +++ b/documentation/docs/03-template-syntax/14-in-and-out.md @@ -17,6 +17,6 @@ The `in:` and `out:` directives are identical to [`transition:`](transition), ex {#if visible} -
flies in, fades out
+
flies in, fades out
{/if} ``` From 6a1b5dd004e494e1e9445a06e320d0873d5566aa Mon Sep 17 00:00:00 2001 From: Devr Date: Thu, 31 Oct 2024 13:54:38 +0530 Subject: [PATCH 068/404] docs : add an example of using dynamic components (#13993) Closes #13990 --------- Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --- documentation/docs/03-template-syntax/05-await.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/documentation/docs/03-template-syntax/05-await.md b/documentation/docs/03-template-syntax/05-await.md index 6f365179e4..06e9bdd540 100644 --- a/documentation/docs/03-template-syntax/05-await.md +++ b/documentation/docs/03-template-syntax/05-await.md @@ -68,3 +68,12 @@ Similarly, if you only want to show the error state, you can omit the `then` blo

The error is {error}

{/await} ``` + +> [!NOTE] You can use `#await` to render dynamic components with the help of the `import()` function: +> +> ```svelte +> {#await import('./Component.svelte') then Component} +> +> {/await} +> ``` + From 217ef2062d2762c380fa98cc7965a88940de80d7 Mon Sep 17 00:00:00 2001 From: Bernie Sumption Date: Thu, 31 Oct 2024 10:25:19 +0000 Subject: [PATCH 069/404] docs: clarify when prop values are deeply reactive (#13994) * Update $props documentation to clarify when prop values are deeply reactive * Update 05-$props.md * Apply suggestions from code review * drive-by-fixes --------- Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --- documentation/docs/02-runes/05-$props.md | 86 +++++++++++++++++++++--- 1 file changed, 77 insertions(+), 9 deletions(-) diff --git a/documentation/docs/02-runes/05-$props.md b/documentation/docs/02-runes/05-$props.md index 8e3dcfb341..17e9b3625f 100644 --- a/documentation/docs/02-runes/05-$props.md +++ b/documentation/docs/02-runes/05-$props.md @@ -5,29 +5,29 @@ title: $props The inputs to a component are referred to as _props_, which is short for _properties_. You pass props to components just like you pass attributes to elements: ```svelte + -/// file: App.svelte ``` On the other side, inside `MyComponent.svelte`, we can receive props with the `$props` rune... ```svelte + -/// file: MyComponent.svelte

this component is {props.adjective}

``` ...though more commonly, you'll [_destructure_](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) your props: ```svelte -/// file: MyComponent.svelte + @@ -40,11 +40,10 @@ On the other side, inside `MyComponent.svelte`, we can receive props with the `$ Destructuring allows us to declare fallback values, which are used if the parent component does not set a given prop: ```js -/// file: MyComponent.svelte let { adjective = 'happy' } = $props(); ``` -> [!NOTE] Fallback values are not turned into reactive state proxies. +> [!NOTE] Fallback values are not turned into reactive state proxies (see [Updating props](#Updating-props) for more info) ## Renaming props @@ -66,9 +65,8 @@ let { a, b, c, ...others } = $props(); References to a prop inside a component update when the prop itself updates — when `count` changes in `App.svelte`, it will also change inside `Child.svelte`. But the child component is able to temporarily override the prop value, which can be useful for unsaved ephemeral state ([demo](/playground/untitled#H4sIAAAAAAAAE6WQ0WrDMAxFf0WIQR0Wmu3VTQJln7HsIfVcZubIxlbGRvC_DzuBraN92qPula50tODZWB1RPi_IX16jLALWSOOUq6P3-_ihLWftNEZ9TVeOWBNHlNhGFYznfqCBzeRdYHh6M_YVzsFNsNs3pdpGd4eBcqPVDMrNxNDBXeSRtXioDgO1zU8ataeZ2RE4Utao924RFXQ9iHXwvoPHKpW1xY4g_Bg0cSVhKS0p560Za95612ZC02ONrD8ZJYdZp_rGQ37ff_mSP86Np2TWZaNNmdcH56P4P67K66_SXoK9pG-5dF5Z9QEAAA==)): - ```svelte -/// file: App.svelte + @@ -94,6 +91,77 @@ References to a prop inside a component update when the prop itself updates — ``` +Prop variables are not automatically deeply reactive. What happens when mutating one of their properties depends on what the parent passed in. For example if the parent passed a non-reactive POJO as a prop, setting a property of that object in the child will not cause the component to update ([demo](/playground/untitled#H4sIAAAAAAAAE3VPS07DMBC9yshCaiuqBLYhjoQ4Q1eEReJOVIMztuJJBbJ8d-IkEqXQ5bx53yCo6VEU4kCs2eBR7EWnDXpRvAbBXy79EjDhK_PZucyf0XDC2sbjf7iyxEg82YjSq0E7rmqqWffODgwvJ22O0A22h02Wz9cq3TzVVOY_CioXrm3fUbEMQdmRuICHGCGvpiDGTxYFDyPG_Y3Cl_6_K199bpQ2yBDWBhBBwp0brPPb3Z-u7chsCSwpo9WHDNsdyApCMslzODUeyAJ23WSUsMUymyfBvYTHmmKcI2e9LyBcUmKKWyKulr_Fb2Z_SHPIAQAA)): + +```svelte + + + + +``` + +```svelte + + + + +``` + +However if the value passed in by the parent component is itself a deeply reactive state object, then it will be deeply reactive in the child, too ([demo](/playground/untitled#H4sIAAAAAAAAE3WQwU7DMBBEf2VlITUVVQLXkERC_YaeCIfE2aoujm3FGwqy_O_YcSug0KNnx7Nv1jHVjchKtlMkSOLANmwvJFpWvjhGnybOohD0s_PZmNy-o6So9Z3F_3SuFaGiEMMqyydhqGlVS2I0eiLYHoQcYD_pEVZ5sbzOX1dPwRaMEgl0f0ROUMOdpY4wc1zPikp48OvgqorvXFWlRJe-eCiawED4QaykaUa_udHl5-rfba4mN_pETHcB9RHVTNrY7C9gPxNpBVpxKfhb7bI11A24GFIUcBJSAu9mi0AHhKUo9Cj1CUjDbIbQP1rTpjzN72t4bJX3C8kSa8vLCZLFR4q0-eogr_4LN7sC9foBAAA=)): + +```svelte + + + + +``` + +```svelte + + + + +``` + +The fallback value of a prop not declared with `$bindable` is treated like a non-reactive POJO, and therefore also doesn't update the component when mutating its properties. + +```svelte + + + +``` + +In general, mutating props is discouraged, instead use callback props to make it easier to reason about state and changes to that state. If parent and child should share (and be allowed to mutate) the same object, then use the [$bindable]($bindable) rune. + ## Type safety You can add type safety to your components by annotating your props, as you would with any other variable declaration. In TypeScript that might look like this... From e47ee226286173db32e09d9f969d34815a1f3d85 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Thu, 31 Oct 2024 11:26:53 +0100 Subject: [PATCH 070/404] fix: wrap `:id`, `:where``:not` and `:has` with `:global` during migration (#13850) Closes #13765 --- .changeset/sour-feet-carry.md | 5 ++ packages/svelte/src/compiler/migrate/index.js | 60 ++++++++++++++++++- .../samples/is-not-where-has/input.svelte | 58 ++++++++++++++++++ .../samples/is-not-where-has/output.svelte | 58 ++++++++++++++++++ 4 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 .changeset/sour-feet-carry.md create mode 100644 packages/svelte/tests/migrate/samples/is-not-where-has/input.svelte create mode 100644 packages/svelte/tests/migrate/samples/is-not-where-has/output.svelte diff --git a/.changeset/sour-feet-carry.md b/.changeset/sour-feet-carry.md new file mode 100644 index 0000000000..6e09bac7f2 --- /dev/null +++ b/.changeset/sour-feet-carry.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: wrap `:id`, `:where``:not` and `:has` with `:global` during migration diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index 5630b8b34c..ee5abc8853 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -34,6 +34,61 @@ class MigrationError extends Error { } } +/** + * + * @param {State} state + */ +function migrate_css(state) { + if (!state.analysis.css.ast?.start) return; + let code = state.str + .snip(state.analysis.css.ast.start, /** @type {number} */ (state.analysis.css.ast?.end)) + .toString(); + let starting = 0; + + // since we already blank css we can't work directly on `state.str` so we will create a copy that we can update + const str = new MagicString(code); + while (code) { + if ( + code.startsWith(':has') || + code.startsWith(':not') || + code.startsWith(':is') || + code.startsWith(':where') + ) { + let start = code.indexOf('(') + 1; + let is_global = false; + const global_str = ':global'; + const next_global = code.indexOf(global_str); + const str_between = code.substring(start, next_global); + if (!str_between.trim()) { + is_global = true; + start += global_str.length; + } + let parenthesis = 1; + let end = start; + let char = code[end]; + // find the closing parenthesis + while (parenthesis !== 0 && char) { + if (char === '(') parenthesis++; + if (char === ')') parenthesis--; + end++; + char = code[end]; + } + if (start && end) { + if (!is_global) { + str.prependLeft(starting + start, ':global('); + str.appendRight(starting + end - 1, ')'); + } + starting += end - 1; + code = code.substring(end - 1); + continue; + } + } + starting++; + code = code.substring(1); + } + state.str.update(state.analysis.css.ast?.start, state.analysis.css.ast?.end, str.toString()); +} + /** * 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. @@ -320,7 +375,10 @@ export function migrate(source, { filename, use_ts } = {}) { if (!parsed.instance && need_script) { str.appendRight(insertion_point, '\n\n\n'); } - return { code: str.toString() }; + migrate_css(state); + return { + code: str.toString() + }; } catch (e) { if (!(e instanceof MigrationError)) { // eslint-disable-next-line no-console diff --git a/packages/svelte/tests/migrate/samples/is-not-where-has/input.svelte b/packages/svelte/tests/migrate/samples/is-not-where-has/input.svelte new file mode 100644 index 0000000000..fbfc1c119f --- /dev/null +++ b/packages/svelte/tests/migrate/samples/is-not-where-has/input.svelte @@ -0,0 +1,58 @@ + + +what if i'm talking about `:has()` in my blog? + +```css + :has(.is_cool) +``` + + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/is-not-where-has/output.svelte b/packages/svelte/tests/migrate/samples/is-not-where-has/output.svelte new file mode 100644 index 0000000000..1e02e9bdfd --- /dev/null +++ b/packages/svelte/tests/migrate/samples/is-not-where-has/output.svelte @@ -0,0 +1,58 @@ + + +what if i'm talking about `:has()` in my blog? + +```css + :has(.is_cool) +``` + + \ No newline at end of file From 3999fed4ca30b35f26b9758afd3240614889e8b7 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 31 Oct 2024 11:02:30 +0000 Subject: [PATCH 071/404] fix: ensure each block inert items are disposed of if the each block is also inert (#13930) Fixes #13926 --- .changeset/curly-dogs-breathe.md | 5 +++ .../src/internal/client/dom/blocks/each.js | 18 ++++++----- .../src/internal/client/reactivity/effects.js | 5 ++- .../samples/transition-each-3/_config.js | 31 +++++++++++++++++++ .../samples/transition-each-3/main.svelte | 28 +++++++++++++++++ 5 files changed, 78 insertions(+), 9 deletions(-) create mode 100644 .changeset/curly-dogs-breathe.md create mode 100644 packages/svelte/tests/runtime-runes/samples/transition-each-3/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/transition-each-3/main.svelte diff --git a/.changeset/curly-dogs-breathe.md b/.changeset/curly-dogs-breathe.md new file mode 100644 index 0000000000..9409c38141 --- /dev/null +++ b/.changeset/curly-dogs-breathe.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure each block inert items are disposed of if the each block is also inert diff --git a/packages/svelte/src/internal/client/dom/blocks/each.js b/packages/svelte/src/internal/client/dom/blocks/each.js index e10aed3867..0fef7d343b 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 } from '../../runtime.js'; +import { active_effect, active_reaction } from '../../runtime.js'; /** * The row of a keyed each block that is currently updating. We track this @@ -204,7 +204,8 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f } if (!hydrating) { - reconcile(array, state, anchor, render_fn, flags, get_key); + var effect = /** @type {Effect} */ (active_reaction); + reconcile(array, state, anchor, render_fn, flags, (effect.f & INERT) !== 0, get_key); } if (fallback_fn !== null) { @@ -248,10 +249,11 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f * @param {Element | Comment | Text} anchor * @param {(anchor: Node, item: MaybeSource, index: number | Source) => void} render_fn * @param {number} flags + * @param {boolean} is_inert * @param {(value: V, index: number) => any} get_key * @returns {void} */ -function reconcile(array, state, anchor, render_fn, flags, get_key) { +function reconcile(array, state, anchor, render_fn, flags, is_inert, get_key) { var is_animated = (flags & EACH_IS_ANIMATED) !== 0; var should_update = (flags & (EACH_ITEM_REACTIVE | EACH_INDEX_REACTIVE)) !== 0; @@ -390,9 +392,9 @@ function reconcile(array, state, anchor, render_fn, flags, get_key) { stashed = []; while (current !== null && current.k !== key) { - // If the item has an effect that is already inert, skip over adding it - // to our seen Set as the item is already being handled - if ((current.e.f & INERT) === 0) { + // 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) { (seen ??= new Set()).add(current); } stashed.push(current); @@ -415,8 +417,8 @@ function reconcile(array, state, anchor, render_fn, flags, get_key) { var to_destroy = seen === undefined ? [] : array_from(seen); while (current !== null) { - // Inert effects are currently outroing and will be removed once the transition is finished - if ((current.e.f & INERT) === 0) { + // 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) { to_destroy.push(current); } current = current.next; diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index daf28eeec2..66b489cf54 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -577,7 +577,6 @@ export function resume_effect(effect) { */ function resume_children(effect, local) { if ((effect.f & INERT) === 0) return; - effect.f ^= INERT; // If a dependency of this effect changed while it was paused, // apply the change now @@ -585,6 +584,10 @@ function resume_children(effect, local) { update_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-3/_config.js b/packages/svelte/tests/runtime-runes/samples/transition-each-3/_config.js new file mode 100644 index 0000000000..ebacf8e8b8 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/transition-each-3/_config.js @@ -0,0 +1,31 @@ +import { flushSync } from '../../../../src/index-client.js'; +import { test } from '../../test'; + +export default test({ + async test({ assert, raf, target }) { + assert.htmlEqual( + target.innerHTML, + '
1
2
3
' + ); + + const btn1 = target.querySelector('button'); + btn1?.click(); + flushSync(); + raf.tick(250); + + assert.htmlEqual( + target.innerHTML, + '
1
2
3
' + ); + + await Promise.resolve(); + + flushSync(); + raf.tick(500); + + assert.htmlEqual( + target.innerHTML, + '
3
4
' + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/transition-each-3/main.svelte b/packages/svelte/tests/runtime-runes/samples/transition-each-3/main.svelte new file mode 100644 index 0000000000..e2ae300dc9 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/transition-each-3/main.svelte @@ -0,0 +1,28 @@ + + + + +{#if toggle} +
+ {#each items as item (item)} +
{item}
+ {/each} +
+{/if} From d93ad3ba1e8c28c2facaec49e19d2ac1ed577e9f Mon Sep 17 00:00:00 2001 From: FoHoOV <53280503+FoHoOV@users.noreply.github.com> Date: Thu, 31 Oct 2024 14:55:49 +0330 Subject: [PATCH 072/404] feat: add `ComponentExports` utility type (#13441) Safer/clearer/backwards/forward-compatibale way to reference them --------- Co-authored-by: Simon Holthausen --- .changeset/rotten-cups-float.md | 5 +++++ packages/svelte/src/index.d.ts | 20 ++++++++++++++++++ packages/svelte/tests/types/component.ts | 26 +++++++++++++++++++++++- packages/svelte/types/index.d.ts | 20 ++++++++++++++++++ 4 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 .changeset/rotten-cups-float.md diff --git a/.changeset/rotten-cups-float.md b/.changeset/rotten-cups-float.md new file mode 100644 index 0000000000..e58d4f26df --- /dev/null +++ b/.changeset/rotten-cups-float.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: provide `ComponentExports` utility type diff --git a/packages/svelte/src/index.d.ts b/packages/svelte/src/index.d.ts index 5130721cbe..262337ef0a 100644 --- a/packages/svelte/src/index.d.ts +++ b/packages/svelte/src/index.d.ts @@ -237,6 +237,26 @@ export type ComponentProps> = ? Props : never; +/** + * Convenience type to get the properties that given component exports. + * + * Example: Typing the `bind:this` for a component named `MyComponent` + * ``` + * + + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/component-type/output.svelte b/packages/svelte/tests/migrate/samples/component-type/output.svelte new file mode 100644 index 0000000000..ab774e56ad --- /dev/null +++ b/packages/svelte/tests/migrate/samples/component-type/output.svelte @@ -0,0 +1,22 @@ + + + \ No newline at end of file From ca58039e3ee44880aefd882f5ef2a1be05d7a9e4 Mon Sep 17 00:00:00 2001 From: Abu <42287254+Anyass3@users.noreply.github.com> Date: Thu, 31 Oct 2024 12:20:57 +0000 Subject: [PATCH 074/404] docs: fix type (#14064) this slot name should be `footer` instead it was `header` --- documentation/docs/07-misc/07-v5-migration-guide.md | 2 +- 1 file changed, 1 insertion(+), 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 1624755a6b..093ec5b86b 100644 --- a/documentation/docs/07-misc/07-v5-migration-guide.md +++ b/documentation/docs/07-misc/07-v5-migration-guide.md @@ -376,7 +376,7 @@ If you wanted multiple UI placeholders, you had to use named slots. In Svelte 5,
- ------ + ------ +++{@render footer()}+++
``` From 4f80ab6da6a0bfe64c4a0d125ba0619461809ac6 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Thu, 31 Oct 2024 14:47:53 +0100 Subject: [PATCH 075/404] fix: allow `warningFilter` option for `compileModule` (#14066) fixes #14012 --- .changeset/curvy-melons-knock.md | 5 +++++ packages/svelte/src/compiler/validate-options.js | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 .changeset/curvy-melons-knock.md diff --git a/.changeset/curvy-melons-knock.md b/.changeset/curvy-melons-knock.md new file mode 100644 index 0000000000..dcf158b4ee --- /dev/null +++ b/.changeset/curvy-melons-knock.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: allow `warningFilter` option for `compileModule` diff --git a/packages/svelte/src/compiler/validate-options.js b/packages/svelte/src/compiler/validate-options.js index c84fdac8a7..9465cba77a 100644 --- a/packages/svelte/src/compiler/validate-options.js +++ b/packages/svelte/src/compiler/validate-options.js @@ -31,7 +31,9 @@ const common = { } return input; - }) + }), + + warningFilter: fun(() => true) }; export const validate_module_options = @@ -106,8 +108,6 @@ export const validate_component_options = hmr: boolean(false), - warningFilter: fun(() => true), - sourcemap: validator(undefined, (input) => { // Source maps can take on a variety of values, including string, JSON, map objects from magic-string and source-map, // so there's no good way to check type validity here From 2784fb19f3f38963b9c5e7d0fbcb7182fd5d589b Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 31 Oct 2024 11:37:30 -0400 Subject: [PATCH 076/404] Revert "feat: add `ComponentExports` utility type (#13441)" (#14069) This reverts commit d93ad3ba1e8c28c2facaec49e19d2ac1ed577e9f. --- .changeset/rotten-cups-float.md | 5 ----- packages/svelte/src/index.d.ts | 20 ------------------ packages/svelte/tests/types/component.ts | 26 +----------------------- packages/svelte/types/index.d.ts | 20 ------------------ 4 files changed, 1 insertion(+), 70 deletions(-) delete mode 100644 .changeset/rotten-cups-float.md diff --git a/.changeset/rotten-cups-float.md b/.changeset/rotten-cups-float.md deleted file mode 100644 index e58d4f26df..0000000000 --- a/.changeset/rotten-cups-float.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -chore: provide `ComponentExports` utility type diff --git a/packages/svelte/src/index.d.ts b/packages/svelte/src/index.d.ts index 262337ef0a..5130721cbe 100644 --- a/packages/svelte/src/index.d.ts +++ b/packages/svelte/src/index.d.ts @@ -237,26 +237,6 @@ export type ComponentProps> = ? Props : never; -/** - * Convenience type to get the properties that given component exports. - * - * Example: Typing the `bind:this` for a component named `MyComponent` - * ``` - * - - \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/component-type/output.svelte b/packages/svelte/tests/migrate/samples/component-type/output.svelte deleted file mode 100644 index ab774e56ad..0000000000 --- a/packages/svelte/tests/migrate/samples/component-type/output.svelte +++ /dev/null @@ -1,22 +0,0 @@ - - - \ No newline at end of file From 3b38bc2a7722de1d594df41736362d4bcdba4202 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2024 11:41:47 -0400 Subject: [PATCH 078/404] Version Packages (#14052) Co-authored-by: github-actions[bot] --- .changeset/curly-dogs-breathe.md | 5 ----- .changeset/curvy-melons-knock.md | 5 ----- .changeset/early-hounds-float.md | 5 ----- .changeset/sour-feet-carry.md | 5 ----- .changeset/wild-parents-begin.md | 5 ----- packages/svelte/CHANGELOG.md | 14 ++++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 8 files changed, 16 insertions(+), 27 deletions(-) delete mode 100644 .changeset/curly-dogs-breathe.md delete mode 100644 .changeset/curvy-melons-knock.md delete mode 100644 .changeset/early-hounds-float.md delete mode 100644 .changeset/sour-feet-carry.md delete mode 100644 .changeset/wild-parents-begin.md diff --git a/.changeset/curly-dogs-breathe.md b/.changeset/curly-dogs-breathe.md deleted file mode 100644 index 9409c38141..0000000000 --- a/.changeset/curly-dogs-breathe.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure each block inert items are disposed of if the each block is also inert diff --git a/.changeset/curvy-melons-knock.md b/.changeset/curvy-melons-knock.md deleted file mode 100644 index dcf158b4ee..0000000000 --- a/.changeset/curvy-melons-knock.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: allow `warningFilter` option for `compileModule` diff --git a/.changeset/early-hounds-float.md b/.changeset/early-hounds-float.md deleted file mode 100644 index 0a708c59e4..0000000000 --- a/.changeset/early-hounds-float.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure onMount correctly fires when new expressions are used diff --git a/.changeset/sour-feet-carry.md b/.changeset/sour-feet-carry.md deleted file mode 100644 index 6e09bac7f2..0000000000 --- a/.changeset/sour-feet-carry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: wrap `:id`, `:where``:not` and `:has` with `:global` during migration diff --git a/.changeset/wild-parents-begin.md b/.changeset/wild-parents-begin.md deleted file mode 100644 index ac98fa0e3e..0000000000 --- a/.changeset/wild-parents-begin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure custom element attribute/prop changes are in their own context diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 1a25f6dc4a..010a1c8e0f 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,19 @@ # svelte +## 5.1.7 + +### Patch Changes + +- fix: ensure each block inert items are disposed of if the each block is also inert ([#13930](https://github.com/sveltejs/svelte/pull/13930)) + +- fix: allow `warningFilter` option for `compileModule` ([#14066](https://github.com/sveltejs/svelte/pull/14066)) + +- fix: ensure onMount correctly fires when new expressions are used ([#14049](https://github.com/sveltejs/svelte/pull/14049)) + +- fix: wrap `:id`, `:where``:not` and `:has` with `:global` during migration ([#13850](https://github.com/sveltejs/svelte/pull/13850)) + +- fix: ensure custom element attribute/prop changes are in their own context ([#14016](https://github.com/sveltejs/svelte/pull/14016)) + ## 5.1.6 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 3612e4e67a..53e1f5e467 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.1.6", + "version": "5.1.7", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index de8344ad66..a17065e9e9 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.1.6'; +export const VERSION = '5.1.7'; export const PUBLIC_VERSION = '5'; From 2bdc3d401f8bce90ff92d8cd1d34c25f61293d7d Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 31 Oct 2024 17:16:06 +0000 Subject: [PATCH 079/404] fix: ensure compiler statements are correctly included (#14074) Fixes #14068 and fixes #14071 --- .changeset/nice-peas-lay.md | 5 +++++ .../3-transform/client/visitors/RegularElement.js | 1 + .../runtime-runes/samples/snippet-ref/_config.js | 8 ++++++++ .../runtime-runes/samples/snippet-ref/main.svelte | 11 +++++++++++ 4 files changed, 25 insertions(+) create mode 100644 .changeset/nice-peas-lay.md create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-ref/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-ref/main.svelte diff --git a/.changeset/nice-peas-lay.md b/.changeset/nice-peas-lay.md new file mode 100644 index 0000000000..f37f92231b --- /dev/null +++ b/.changeset/nice-peas-lay.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure compiler statements are correctly included 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 bd881e0f76..6ef29b621f 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 @@ -401,6 +401,7 @@ export function RegularElement(node, context) { context.state.init.push( b.block([ ...child_state.init, + ...element_state.init, child_state.update.length > 0 ? build_render_statement(child_state.update) : b.empty, ...child_state.after_update, ...element_state.after_update diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-ref/_config.js b/packages/svelte/tests/runtime-runes/samples/snippet-ref/_config.js new file mode 100644 index 0000000000..9dc0bd3bd5 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-ref/_config.js @@ -0,0 +1,8 @@ +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true // Render in dev mode to check that the validation error is not thrown + }, + html: `
\nundefined` +}); diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-ref/main.svelte b/packages/svelte/tests/runtime-runes/samples/snippet-ref/main.svelte new file mode 100644 index 0000000000..7e4e6fd0aa --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-ref/main.svelte @@ -0,0 +1,11 @@ + + +
+ {#snippet Bug()} + cool + {/snippet} +
+ +{typeof thisBug} From b0d255aefe6e5071c0cde23285ff0e89eec23080 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2024 18:18:06 +0100 Subject: [PATCH 080/404] Version Packages (#14075) Co-authored-by: github-actions[bot] --- .changeset/nice-peas-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/nice-peas-lay.md diff --git a/.changeset/nice-peas-lay.md b/.changeset/nice-peas-lay.md deleted file mode 100644 index f37f92231b..0000000000 --- a/.changeset/nice-peas-lay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure compiler statements are correctly included diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 010a1c8e0f..ac474979c6 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.1.8 + +### Patch Changes + +- fix: ensure compiler statements are correctly included ([#14074](https://github.com/sveltejs/svelte/pull/14074)) + ## 5.1.7 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 53e1f5e467..394f943894 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.1.7", + "version": "5.1.8", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index a17065e9e9..6386be520d 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.1.7'; +export const VERSION = '5.1.8'; export const PUBLIC_VERSION = '5'; From 0ed914b2e54dd14df77f672d4d3300bc80f200af Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 31 Oct 2024 14:10:47 -0400 Subject: [PATCH 081/404] docs: improve lazy import example (#14076) * docs: improve lazy import example * Update documentation/docs/03-template-syntax/05-await.md --- documentation/docs/03-template-syntax/05-await.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/docs/03-template-syntax/05-await.md b/documentation/docs/03-template-syntax/05-await.md index 06e9bdd540..842b3c7e32 100644 --- a/documentation/docs/03-template-syntax/05-await.md +++ b/documentation/docs/03-template-syntax/05-await.md @@ -69,11 +69,11 @@ Similarly, if you only want to show the error state, you can omit the `then` blo {/await} ``` -> [!NOTE] You can use `#await` to render dynamic components with the help of the `import()` function: +> [!NOTE] You can use `#await` with [`import(...)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import) to render components lazily: > > ```svelte -> {#await import('./Component.svelte') then Component} -> +> {#await import('./Component.svelte') then { default: Component }} +> > {/await} > ``` From 58b1540ddc52e52215eb41e20db58c6a796cc329 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 31 Oct 2024 18:36:21 +0000 Subject: [PATCH 082/404] fix: ensure transitions are applied to nested elements (#14080) --- .changeset/modern-adults-lay.md | 5 ++++ .../src/compiler/phases/2-analyze/index.js | 2 ++ .../2-analyze/visitors/TransitionDirective.js | 14 +++++++++++ .../transition-static-subtree/_config.js | 23 +++++++++++++++++++ .../transition-static-subtree/main.svelte | 18 +++++++++++++++ 5 files changed, 62 insertions(+) create mode 100644 .changeset/modern-adults-lay.md create mode 100644 packages/svelte/src/compiler/phases/2-analyze/visitors/TransitionDirective.js create mode 100644 packages/svelte/tests/runtime-runes/samples/transition-static-subtree/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/transition-static-subtree/main.svelte diff --git a/.changeset/modern-adults-lay.md b/.changeset/modern-adults-lay.md new file mode 100644 index 0000000000..9caa3962af --- /dev/null +++ b/.changeset/modern-adults-lay.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure transitions are applied to nested elements diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 89c2e19c3f..f5a31d09a7 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -63,6 +63,7 @@ import { SvelteWindow } from './visitors/SvelteWindow.js'; import { TaggedTemplateExpression } from './visitors/TaggedTemplateExpression.js'; import { Text } from './visitors/Text.js'; 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 { VariableDeclarator } from './visitors/VariableDeclarator.js'; @@ -172,6 +173,7 @@ const visitors = { SvelteWindow, TaggedTemplateExpression, Text, + TransitionDirective, TitleElement, UpdateExpression, UseDirective, diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/TransitionDirective.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/TransitionDirective.js new file mode 100644 index 0000000000..c218f741c3 --- /dev/null +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/TransitionDirective.js @@ -0,0 +1,14 @@ +/** @import { AST } from '#compiler' */ +/** @import { Context } from '../types' */ + +import { mark_subtree_dynamic } from './shared/fragment.js'; + +/** + * @param {AST.TransitionDirective} node + * @param {Context} context + */ +export function TransitionDirective(node, context) { + mark_subtree_dynamic(context.path); + + context.next(); +} diff --git a/packages/svelte/tests/runtime-runes/samples/transition-static-subtree/_config.js b/packages/svelte/tests/runtime-runes/samples/transition-static-subtree/_config.js new file mode 100644 index 0000000000..99563ef959 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/transition-static-subtree/_config.js @@ -0,0 +1,23 @@ +import { flushSync } from '../../../../src/index-client.js'; +import { test } from '../../test'; + +export default test({ + async test({ assert, raf, target }) { + assert.htmlEqual(target.innerHTML, '
123
'); + + const btn1 = target.querySelector('button'); + btn1?.click(); + flushSync(); + raf.tick(250); + + assert.htmlEqual( + target.innerHTML, + '
123
' + ); + + flushSync(); + raf.tick(500); + + assert.htmlEqual(target.innerHTML, ''); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/transition-static-subtree/main.svelte b/packages/svelte/tests/runtime-runes/samples/transition-static-subtree/main.svelte new file mode 100644 index 0000000000..4a15e15699 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/transition-static-subtree/main.svelte @@ -0,0 +1,18 @@ + + + + +{#if visible} +
+ 123 +
+{/if} From 9cd476881dfec0a2bf6d59c28c233e79d6548946 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 31 Oct 2024 14:36:54 -0400 Subject: [PATCH 083/404] fix props docs (#14079) --- documentation/docs/02-runes/05-$props.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/documentation/docs/02-runes/05-$props.md b/documentation/docs/02-runes/05-$props.md index 17e9b3625f..58e9b36f8e 100644 --- a/documentation/docs/02-runes/05-$props.md +++ b/documentation/docs/02-runes/05-$props.md @@ -91,17 +91,17 @@ References to a prop inside a component update when the prop itself updates — ``` -Prop variables are not automatically deeply reactive. What happens when mutating one of their properties depends on what the parent passed in. For example if the parent passed a non-reactive POJO as a prop, setting a property of that object in the child will not cause the component to update ([demo](/playground/untitled#H4sIAAAAAAAAE3VPS07DMBC9yshCaiuqBLYhjoQ4Q1eEReJOVIMztuJJBbJ8d-IkEqXQ5bx53yCo6VEU4kCs2eBR7EWnDXpRvAbBXy79EjDhK_PZucyf0XDC2sbjf7iyxEg82YjSq0E7rmqqWffODgwvJ22O0A22h02Wz9cq3TzVVOY_CioXrm3fUbEMQdmRuICHGCGvpiDGTxYFDyPG_Y3Cl_6_K199bpQ2yBDWBhBBwp0brPPb3Z-u7chsCSwpo9WHDNsdyApCMslzODUeyAJ23WSUsMUymyfBvYTHmmKcI2e9LyBcUmKKWyKulr_Fb2Z_SHPIAQAA)): +While you can temporarily _reassign_ props, you should not _mutate_ props unless they are [bindable]($bindable). + +If the prop is a regular object, the mutation will have no effect ([demo](/playground/untitled#H4sIAAAAAAAAE3WQwU7DMBBEf2W1QmorQgJXk0RC3PkBwiExG9WQrC17U4Es_ztKUkQp9OjxzM7bjcjtSKjwyfKNp1aLORA4b13ADHszUED1HFE-3eyaBcy-Mw_O5eFAg8xa1wb6T9eWhVgCKiyD9sZJ3XAjZnTWCzzuzfAKvbcjbPJieR2jm_uGy-InweXqtd0baaliBG0nFgW3kBIUNWYo9CGoxE-UsgvIpw2_oc9-LmAPJBCPDJCggqvlVtvdH9puErEMlvVg9HsVtzuoaojzkKKAfRuALVDfk5ZZW0fmy05wXcFdwyktlUs-KIinljTXrRVnm7-kL9dYLVbUAQAA)): ```svelte - + ``` ```svelte @@ -118,7 +118,7 @@ Prop variables are not automatically deeply reactive. What happens when mutating ``` -However if the value passed in by the parent component is itself a deeply reactive state object, then it will be deeply reactive in the child, too ([demo](/playground/untitled#H4sIAAAAAAAAE3WQwU7DMBBEf2VlITUVVQLXkERC_YaeCIfE2aoujm3FGwqy_O_YcSug0KNnx7Nv1jHVjchKtlMkSOLANmwvJFpWvjhGnybOohD0s_PZmNy-o6So9Z3F_3SuFaGiEMMqyydhqGlVS2I0eiLYHoQcYD_pEVZ5sbzOX1dPwRaMEgl0f0ROUMOdpY4wc1zPikp48OvgqorvXFWlRJe-eCiawED4QaykaUa_udHl5-rfba4mN_pETHcB9RHVTNrY7C9gPxNpBVpxKfhb7bI11A24GFIUcBJSAu9mi0AHhKUo9Cj1CUjDbIbQP1rTpjzN72t4bJX3C8kSa8vLCZLFR4q0-eogr_4LN7sC9foBAAA=)): +If the prop is a reactive state proxy, however, then mutations _will_ have an effect but you will see an [`ownership_invalid_mutation`](runtime-warnings#Client-warnings-ownership_invalid_mutation) warning, because the component is mutating state that does not 'belong' to it ([demo](/playground/untitled#H4sIAAAAAAAAE3WR0U7DMAxFf8VESBuiauG1WycheOEbKA9p67FA6kSNszJV-XeUZhMw2GN8r-1znUmQ7FGU4pn2UqsOes-SlSGRia3S6ET5Mgk-2OiJBZGdOh6szd0eNcdaIx3-V28NMRI7UYq1awdleVNTzaq3ZmB43CndwXYwPSzyYn4dWxermqJRI4Np3rFlqODasWRcTtAaT1zCHYSbVU3r4nsyrdPMKTUFKDYiE4yfLEoePIbsQpqfy3_nOVMuJIqg0wk1RFg7GOuWfwEbz2wIDLVatR_VtLyBagNTHFIUMCqtoZXeIfAOU1JoUJsR2IC3nWTMjt7GM4yKdyBhlAMpesvhydCC0y_i0ZagHByMh26WzUhXUUxKnpbcVnBfUwhznJnNlac7JkuIURL-2VVfwxflyrWcSQIAAA==)): ```svelte @@ -138,20 +138,23 @@ However if the value passed in by the parent component is itself a deeply reacti ``` -The fallback value of a prop not declared with `$bindable` is treated like a non-reactive POJO, and therefore also doesn't update the component when mutating its properties. +The fallback value of a prop not declared with `$bindable` is left untouched — it is not turned into a reactive state proxy — meaning mutations will not cause updates ([demo](/playground/untitled#H4sIAAAAAAAAE3WQwU7DMBBEf2VkIbUVoYFraCIh7vwA4eC4G9Wta1vxpgJZ_nfkBEQp9OjxzOzTRGHlkUQlXpy9G0gq1idCL43ppDrAD84HUYheGwqieo2CP3y2Z0EU3-En79fhRIaz1slA_-nKWSbLQVRiE9SgPTetbVkfvRsYzztttugHd8RiXU6vr-jisbWb8idhN7O3bEQhmN5ZVDyMlIorcOddv_Eufq4AGmJEuG5PilEjQrnRcoV7JCTUuJlGWq7-YHYjs7NwVhmtDnVcrlA3iLmzLLGTAdaB-j736h68Oxv-JM1I0AFjoG1OzPfX023c1nhobUoT39QeKsRzS8owM8DFTG_pE6dcVl70AQAA)) ```svelte + ``` -In general, mutating props is discouraged, instead use callback props to make it easier to reason about state and changes to that state. If parent and child should share (and be allowed to mutate) the same object, then use the [$bindable]($bindable) rune. +In summary: don't mutate props. Either use callback props to communicate changes, or — if parent and child should share the same object — use the [`$bindable`]($bindable) rune. ## Type safety From e58c03b100c6b2cb0bbdcb50453082d40b4b9864 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 31 Oct 2024 14:37:32 -0400 Subject: [PATCH 084/404] add link to sync-docs README (#14056) --- .github/workflows/sync-request.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/sync-request.yml b/.github/workflows/sync-request.yml index a4147faf73..de2ce77692 100644 --- a/.github/workflows/sync-request.yml +++ b/.github/workflows/sync-request.yml @@ -1,3 +1,4 @@ +# https://github.com/sveltejs/svelte.dev/blob/main/apps/svelte.dev/scripts/sync-docs/README.md name: Sync request on: From ffbd5c971e67e98a55745885ab01cb28c9b82228 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 31 Oct 2024 14:52:56 -0400 Subject: [PATCH 085/404] docs: fix runtime-warnings page (#14077) * fix runtime-warnings page * fix --- documentation/docs/98-reference/30-runtime-warnings.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/documentation/docs/98-reference/30-runtime-warnings.md b/documentation/docs/98-reference/30-runtime-warnings.md index 3e040eb06f..edc5890ac0 100644 --- a/documentation/docs/98-reference/30-runtime-warnings.md +++ b/documentation/docs/98-reference/30-runtime-warnings.md @@ -2,10 +2,10 @@ title: 'Runtime warnings' --- -## Client errors +## Client warnings -@include .generated/client-errors.md +@include .generated/client-warnings.md -## Shared errors +## Shared warnings -@include .generated/shared-errors.md +@include .generated/shared-warnings.md From cf25930512417f4c3f15c2abade95c929569e484 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2024 19:40:21 +0000 Subject: [PATCH 086/404] Version Packages (#14081) Co-authored-by: github-actions[bot] --- .changeset/modern-adults-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/modern-adults-lay.md diff --git a/.changeset/modern-adults-lay.md b/.changeset/modern-adults-lay.md deleted file mode 100644 index 9caa3962af..0000000000 --- a/.changeset/modern-adults-lay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure transitions are applied to nested elements diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index ac474979c6..0786d007f3 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.1.9 + +### Patch Changes + +- fix: ensure transitions are applied to nested elements ([#14080](https://github.com/sveltejs/svelte/pull/14080)) + ## 5.1.8 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 394f943894..257d3924bf 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.1.8", + "version": "5.1.9", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 6386be520d..e6a9988e3a 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.1.8'; +export const VERSION = '5.1.9'; export const PUBLIC_VERSION = '5'; From 2ab70041fd27801f9cf7f6bd1e59df7eb3b6c50a Mon Sep 17 00:00:00 2001 From: Conduitry Date: Thu, 31 Oct 2024 22:44:39 -0400 Subject: [PATCH 087/404] chore: mark markdown output from process-messages as generated (#13913) * chore: mark markdown output from process-messages as generated * regenerate --- .../98-reference/.generated/client-errors.md | 2 ++ .../.generated/client-warnings.md | 2 ++ .../98-reference/.generated/compile-errors.md | 2 ++ .../.generated/compile-warnings.md | 2 ++ .../98-reference/.generated/server-errors.md | 2 ++ .../98-reference/.generated/shared-errors.md | 2 ++ .../.generated/shared-warnings.md | 2 ++ .../svelte/scripts/process-messages/index.js | 22 ++++++++++--------- 8 files changed, 26 insertions(+), 10 deletions(-) diff --git a/documentation/docs/98-reference/.generated/client-errors.md b/documentation/docs/98-reference/.generated/client-errors.md index 9879b67b33..90c9c1f9d1 100644 --- a/documentation/docs/98-reference/.generated/client-errors.md +++ b/documentation/docs/98-reference/.generated/client-errors.md @@ -1,3 +1,5 @@ + + ### bind_invalid_checkbox_value ``` diff --git a/documentation/docs/98-reference/.generated/client-warnings.md b/documentation/docs/98-reference/.generated/client-warnings.md index 743a47b67a..e9f1a88c3c 100644 --- a/documentation/docs/98-reference/.generated/client-warnings.md +++ b/documentation/docs/98-reference/.generated/client-warnings.md @@ -1,3 +1,5 @@ + + ### binding_property_non_reactive ``` diff --git a/documentation/docs/98-reference/.generated/compile-errors.md b/documentation/docs/98-reference/.generated/compile-errors.md index 3981822d84..b32bc6b89c 100644 --- a/documentation/docs/98-reference/.generated/compile-errors.md +++ b/documentation/docs/98-reference/.generated/compile-errors.md @@ -1,3 +1,5 @@ + + ### animation_duplicate ``` diff --git a/documentation/docs/98-reference/.generated/compile-warnings.md b/documentation/docs/98-reference/.generated/compile-warnings.md index e2ac86be2f..775e0681c9 100644 --- a/documentation/docs/98-reference/.generated/compile-warnings.md +++ b/documentation/docs/98-reference/.generated/compile-warnings.md @@ -1,3 +1,5 @@ + + ### a11y_accesskey ``` diff --git a/documentation/docs/98-reference/.generated/server-errors.md b/documentation/docs/98-reference/.generated/server-errors.md index f325b5349a..8cf6727da9 100644 --- a/documentation/docs/98-reference/.generated/server-errors.md +++ b/documentation/docs/98-reference/.generated/server-errors.md @@ -1,3 +1,5 @@ + + ### lifecycle_function_unavailable ``` diff --git a/documentation/docs/98-reference/.generated/shared-errors.md b/documentation/docs/98-reference/.generated/shared-errors.md index 1a2bcf8d2d..52ef9d5f88 100644 --- a/documentation/docs/98-reference/.generated/shared-errors.md +++ b/documentation/docs/98-reference/.generated/shared-errors.md @@ -1,3 +1,5 @@ + + ### invalid_default_snippet ``` diff --git a/documentation/docs/98-reference/.generated/shared-warnings.md b/documentation/docs/98-reference/.generated/shared-warnings.md index 4ff3f89e6d..e9327e1e98 100644 --- a/documentation/docs/98-reference/.generated/shared-warnings.md +++ b/documentation/docs/98-reference/.generated/shared-warnings.md @@ -1,3 +1,5 @@ + + ### dynamic_void_element_content ``` diff --git a/packages/svelte/scripts/process-messages/index.js b/packages/svelte/scripts/process-messages/index.js index 3a4e9d8a4d..3360b24802 100644 --- a/packages/svelte/scripts/process-messages/index.js +++ b/packages/svelte/scripts/process-messages/index.js @@ -62,18 +62,20 @@ for (const category of fs.readdirSync('messages')) { fs.writeFileSync( `${DIR}/${category}.md`, - Object.entries(messages[category]) - .map(([code, { messages, details }]) => { - const chunks = [`### ${code}`, ...messages.map((message) => '```\n' + message + '\n```')]; + '\n\n' + + Object.entries(messages[category]) + .map(([code, { messages, details }]) => { + const chunks = [`### ${code}`, ...messages.map((message) => '```\n' + message + '\n```')]; - if (details) { - chunks.push(details); - } + if (details) { + chunks.push(details); + } - return chunks.join('\n\n'); - }) - .sort() - .join('\n\n') + '\n' + return chunks.join('\n\n'); + }) + .sort() + .join('\n\n') + + '\n' ); } From 6a2c28c59091f93c483ed4a77d2eaf617d602ae8 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 1 Nov 2024 03:49:52 -0400 Subject: [PATCH 088/404] docs: tweak untrack description, provide an example of usage (#14085) * docs: tweak untrack description, provide an example of usage * link to untrack * add derived docs too --- documentation/docs/02-runes/03-$derived.md | 6 ++++++ documentation/docs/02-runes/04-$effect.md | 2 +- packages/svelte/src/internal/client/runtime.js | 12 +++++++++++- packages/svelte/types/index.d.ts | 12 +++++++++++- 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/documentation/docs/02-runes/03-$derived.md b/documentation/docs/02-runes/03-$derived.md index 77ab07a0a9..6b38f99746 100644 --- a/documentation/docs/02-runes/03-$derived.md +++ b/documentation/docs/02-runes/03-$derived.md @@ -45,3 +45,9 @@ Sometimes you need to create complex derivations that don't fit inside a short e ``` In essence, `$derived(expression)` is equivalent to `$derived.by(() => expression)`. + +## Understanding dependencies + +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). diff --git a/documentation/docs/02-runes/04-$effect.md b/documentation/docs/02-runes/04-$effect.md index 354869f35e..3515a6c4e4 100644 --- a/documentation/docs/02-runes/04-$effect.md +++ b/documentation/docs/02-runes/04-$effect.md @@ -2,7 +2,7 @@ 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, and re-runs the function when that state later changes. +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. Most of the effects in a Svelte app are created by Svelte itself — they're the bits that update the text in `

hello {name}!

` when `name` changes, for example. diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 6bed3825c7..bd4d23dd15 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -831,7 +831,17 @@ export function invalidate_inner_signals(fn) { } /** - * Use `untrack` to prevent something from being treated as an `$effect`/`$derived` dependency. + * 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) + * }); + * }); + * ``` * @template T * @param {() => T} fn * @returns {T} diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index b4da4c79b8..0fc0e7ad16 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -455,7 +455,17 @@ declare module 'svelte' { * */ export function tick(): Promise; /** - * Use `untrack` to prevent something from being treated as an `$effect`/`$derived` dependency. + * 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(fn: () => T): T; /** From 7d11fa8da2f97be96fa9353bd28981d946b06903 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Fri, 1 Nov 2024 13:19:52 +0100 Subject: [PATCH 089/404] fix: recognize all custom element prop definitions (#14084) We didn't account for the `$props` rune being writtin in a way that makes some props unknown, and they would only be visible through the `customElement.props` definition. This changes the iteration to account for that and also adds a note to the documentation that you need to list out the properties explicitly. fixes #13785 --- .changeset/warm-eyes-protect.md | 5 +++++ .../docs/07-misc/04-custom-elements.md | 2 ++ .../3-transform/client/transform-client.js | 20 +++++++++++++----- .../props-rune-attributes/_config.js | 21 +++++++++++++++++++ .../props-rune-attributes/main.svelte | 14 +++++++++++++ .../props-rune-attributes/my-widget.svelte | 14 +++++++++++++ 6 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 .changeset/warm-eyes-protect.md create mode 100644 packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/_config.js create mode 100644 packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/main.svelte create mode 100644 packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/my-widget.svelte diff --git a/.changeset/warm-eyes-protect.md b/.changeset/warm-eyes-protect.md new file mode 100644 index 0000000000..fbb971f314 --- /dev/null +++ b/.changeset/warm-eyes-protect.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: recognize all custom element prop definitions diff --git a/documentation/docs/07-misc/04-custom-elements.md b/documentation/docs/07-misc/04-custom-elements.md index 83874f2f5b..71c66f7edc 100644 --- a/documentation/docs/07-misc/04-custom-elements.md +++ b/documentation/docs/07-misc/04-custom-elements.md @@ -49,6 +49,8 @@ console.log(el.name); el.name = 'everybody'; ``` +Note that you need to list out all properties explicitly, i.e. doing `let props = $props()` without declaring `props` in the [component options](#Component-options) means that Svelte can't know which props to expose as properties on the DOM element. + ## Component lifecycle Custom elements are created from Svelte components using a wrapper approach. This means the inner Svelte component has no knowledge that it is a custom element. The custom element wrapper takes care of handling its lifecycle appropriately. 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 8369842e13..4daff53efb 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 @@ -561,17 +561,19 @@ export function client_component(analysis, options) { if (analysis.custom_element) { const ce = analysis.custom_element; + const ce_props = typeof ce === 'boolean' ? {} : ce.props || {}; /** @type {ESTree.Property[]} */ const props_str = []; - for (const [name, binding] of properties) { - const key = binding.prop_alias ?? name; - const prop_def = typeof ce === 'boolean' ? {} : ce.props?.[key] || {}; + for (const [name, prop_def] of Object.entries(ce_props)) { + const binding = analysis.instance.scope.get(name); + const key = binding?.prop_alias ?? name; + if ( !prop_def.type && - binding.initial?.type === 'Literal' && - typeof binding.initial.value === 'boolean' + binding?.initial?.type === 'Literal' && + typeof binding?.initial.value === 'boolean' ) { prop_def.type = 'Boolean'; } @@ -585,9 +587,17 @@ export function client_component(analysis, options) { ].filter(Boolean) ) ); + props_str.push(b.init(key, value)); } + for (const [name, binding] of properties) { + const key = binding.prop_alias ?? name; + if (ce_props[key]) continue; + + props_str.push(b.init(key, b.object([]))); + } + const slots_str = b.array([...analysis.slot_names.keys()].map((name) => b.literal(name))); const accessors_str = b.array( analysis.exports.map(({ name, alias }) => b.literal(alias ?? name)) diff --git a/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/_config.js b/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/_config.js new file mode 100644 index 0000000000..04b49c5c00 --- /dev/null +++ b/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/_config.js @@ -0,0 +1,21 @@ +import { test } from '../../assert'; +const tick = () => Promise.resolve(); + +export default test({ + async test({ assert, target }) { + target.innerHTML = ''; + await tick(); + + /** @type {any} */ + const el = target.querySelector('custom-element'); + + assert.htmlEqual( + el.shadowRoot.innerHTML, + ` +

1

+

2

+

3

+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/main.svelte b/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/main.svelte new file mode 100644 index 0000000000..bea04b1248 --- /dev/null +++ b/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/main.svelte @@ -0,0 +1,14 @@ + + + + +

{rest.foo}

+

{bar}

+

{baz}

diff --git a/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/my-widget.svelte b/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/my-widget.svelte new file mode 100644 index 0000000000..cdba7491f9 --- /dev/null +++ b/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/my-widget.svelte @@ -0,0 +1,14 @@ + + + + +

{rest.foo}

+

{bar}

+

{baz}

From a315a14939fbbd2bf51564af77e4415eaacfab4a Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Fri, 1 Nov 2024 12:52:32 +0000 Subject: [PATCH 090/404] fix: ensure video elements autoplay in safari (#14095) --- .changeset/empty-hairs-love.md | 5 +++++ .../phases/3-transform/client/visitors/RegularElement.js | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 .changeset/empty-hairs-love.md diff --git a/.changeset/empty-hairs-love.md b/.changeset/empty-hairs-love.md new file mode 100644 index 0000000000..a56e6c6334 --- /dev/null +++ b/.changeset/empty-hairs-love.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure video elements autoplay in safari 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 6ef29b621f..dafe1e74ae 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 @@ -61,11 +61,13 @@ export function RegularElement(node, context) { const is_custom_element = is_custom_element_node(node); - if (is_custom_element) { + if (node.name === 'video' || is_custom_element) { // cloneNode is faster, but it does not instantiate the underlying class of the // custom element until the template is connected to the dom, which would // cause problems when setting properties on the custom element. // Therefore we need to use importNode instead, which doesn't have this caveat. + // Additionally, Webkit browsers need importNode for video elements for autoplay + // to work correctly. context.state.metadata.context.template_needs_import_node = true; } From e7d68d8c10d4864cb4c58cd86f6076c187b22ec0 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Fri, 1 Nov 2024 14:22:24 +0100 Subject: [PATCH 091/404] fix: pass along `anchor` in legacy class wrappers (#14100) fixes #13832 --- .changeset/silly-bulldogs-rest.md | 5 +++++ packages/svelte/src/legacy/legacy-client.js | 1 + 2 files changed, 6 insertions(+) create mode 100644 .changeset/silly-bulldogs-rest.md diff --git a/.changeset/silly-bulldogs-rest.md b/.changeset/silly-bulldogs-rest.md new file mode 100644 index 0000000000..f92eb81575 --- /dev/null +++ b/.changeset/silly-bulldogs-rest.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: pass along `anchor` in legacy class wrappers diff --git a/packages/svelte/src/legacy/legacy-client.js b/packages/svelte/src/legacy/legacy-client.js index 4148f3d1cd..c7684570bb 100644 --- a/packages/svelte/src/legacy/legacy-client.js +++ b/packages/svelte/src/legacy/legacy-client.js @@ -110,6 +110,7 @@ class Svelte4Component { this.#instance = (options.hydrate ? hydrate : mount)(options.component, { target: options.target, + anchor: options.anchor, props, context: options.context, intro: options.intro ?? false, From 535ea44b9da3042ddb887070a1467747421a4d6c Mon Sep 17 00:00:00 2001 From: Conduitry Date: Fri, 1 Nov 2024 12:08:04 -0400 Subject: [PATCH 092/404] chore: replace non-breaking spaces (#14098) * replace nbsp in docs * replace nbsp in test * replace nbsp in types * regenerate types --- documentation/docs/03-template-syntax/11-bind.md | 2 +- documentation/docs/03-template-syntax/13-transition.md | 3 +-- .../docs/99-legacy/02-legacy-reactive-assignments.md | 2 +- documentation/docs/99-legacy/40-legacy-component-api.md | 2 +- packages/svelte/src/ambient.d.ts | 4 ++-- .../samples/dynamic-element-store/main.svelte | 6 +++--- packages/svelte/types/index.d.ts | 4 ++-- 7 files changed, 11 insertions(+), 12 deletions(-) diff --git a/documentation/docs/03-template-syntax/11-bind.md b/documentation/docs/03-template-syntax/11-bind.md index 86a725f2f9..4fe3e7c4a8 100644 --- a/documentation/docs/03-template-syntax/11-bind.md +++ b/documentation/docs/03-template-syntax/11-bind.md @@ -230,7 +230,7 @@ All visible elements have the following readonly bindings, measured with a `Resi bind:this={dom_node} ``` -To get a reference to a DOM node, use `bind:this`. The value will be `undefined` until the component is mounted — in other words, you should read it inside an effect or an event handler, but not during component initialisation: +To get a reference to a DOM node, use `bind:this`. The value will be `undefined` until the component is mounted — in other words, you should read it inside an effect or an event handler, but not during component initialisation: ```svelte - \ No newline at end of file + diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 0fc0e7ad16..0f08fabfcb 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -2354,7 +2354,7 @@ declare namespace $state { | BigInt64Array | BigUint64Array; - /** The things that `structuredClone` can handle — https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm */ + /** The things that `structuredClone` can handle — https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm */ export type Cloneable = | ArrayBuffer | DataView @@ -2665,7 +2665,7 @@ declare function $props(): any; declare function $bindable(fallback?: T): T; /** - * Inspects one or more values whenever they, or the properties they contain, change. Example: + * Inspects one or more values whenever they, or the properties they contain, change. Example: * * ```ts * $inspect(someValue, someOtherValue) From 9883e7078cfedf67b12302b8e202519794293b3d Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Fri, 1 Nov 2024 16:08:45 +0000 Subject: [PATCH 093/404] fix: ensure SvelteMap and SvelteSet work with generators in dev (#14103) * fix: ensure SvelteMap and SvelteSet work with generators in dev * add test * better fix * better fix * better fix * better fix --- .changeset/selfish-chicken-argue.md | 5 +++++ packages/svelte/src/reactivity/map.js | 2 +- packages/svelte/src/reactivity/set.js | 2 +- .../samples/svelte-set-generators/_config.js | 9 +++++++++ .../samples/svelte-set-generators/main.svelte | 14 ++++++++++++++ 5 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 .changeset/selfish-chicken-argue.md create mode 100644 packages/svelte/tests/runtime-runes/samples/svelte-set-generators/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/svelte-set-generators/main.svelte diff --git a/.changeset/selfish-chicken-argue.md b/.changeset/selfish-chicken-argue.md new file mode 100644 index 0000000000..32e0d0afef --- /dev/null +++ b/.changeset/selfish-chicken-argue.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure SvelteMap and SvelteSet work with generators in dev diff --git a/packages/svelte/src/reactivity/map.js b/packages/svelte/src/reactivity/map.js index ae1bf47773..3fa2945ef0 100644 --- a/packages/svelte/src/reactivity/map.js +++ b/packages/svelte/src/reactivity/map.js @@ -22,7 +22,7 @@ export class SvelteMap extends Map { super(); // If the value is invalid then the native exception will fire here - if (DEV) new Map(value); + if (DEV) value = new Map(value); if (value) { for (var [key, v] of value) { diff --git a/packages/svelte/src/reactivity/set.js b/packages/svelte/src/reactivity/set.js index 38723a0b9e..be0c2d2cf5 100644 --- a/packages/svelte/src/reactivity/set.js +++ b/packages/svelte/src/reactivity/set.js @@ -26,7 +26,7 @@ export class SvelteSet extends Set { super(); // If the value is invalid then the native exception will fire here - if (DEV) new Set(value); + if (DEV) value = new Set(value); if (value) { for (var element of value) { diff --git a/packages/svelte/tests/runtime-runes/samples/svelte-set-generators/_config.js b/packages/svelte/tests/runtime-runes/samples/svelte-set-generators/_config.js new file mode 100644 index 0000000000..8c6279baa3 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/svelte-set-generators/_config.js @@ -0,0 +1,9 @@ +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + }, + + html: `1` +}); diff --git a/packages/svelte/tests/runtime-runes/samples/svelte-set-generators/main.svelte b/packages/svelte/tests/runtime-runes/samples/svelte-set-generators/main.svelte new file mode 100644 index 0000000000..632dc8a588 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/svelte-set-generators/main.svelte @@ -0,0 +1,14 @@ + + +{#each gen as item} + {item} +{/each} + From aac4cbd1f1c854cbc5e72d824538077a1234d07e Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Fri, 1 Nov 2024 17:21:36 +0100 Subject: [PATCH 094/404] docs: note browser condition earlier (#14096) * docs: note browser condition earlier The browser condition is also necessary to test runes, so it makes sense to add it to the first occurence to the vite config. Also add a note about more fine-grained alias conditions. Closes #13961 * Update documentation/docs/07-misc/02-testing.md --------- Co-authored-by: Rich Harris --- documentation/docs/07-misc/02-testing.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/documentation/docs/07-misc/02-testing.md b/documentation/docs/07-misc/02-testing.md index f0ed52a3c9..7609242471 100644 --- a/documentation/docs/07-misc/02-testing.md +++ b/documentation/docs/07-misc/02-testing.md @@ -21,9 +21,18 @@ Then adjust your `vite.config.js`: /// file: vite.config.js import { defineConfig } from +++'vitest/config'+++; -export default defineConfig({ /* ... */ }) +export default defineConfig({ + // ... + // Tell Vitest to use the `browser` entry points in `package.json` files, even though it's running in Node + resolve: process.env.VITEST + ? { + conditions: ['browser'] + } + : undefined ``` +> [!NOTE] If loading the browser version of all your packages is undesirable, because (for example) you also test backend libraries, [you may need to resort to an alias configuration](https://github.com/testing-library/svelte-testing-library/issues/222#issuecomment-1909993331) + You can now write unit tests for code inside your `.js/.ts` files: ```js From 82b87b361e2004b58f683be3c70d678ed8f29e59 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 1 Nov 2024 12:48:04 -0400 Subject: [PATCH 095/404] docs: fix syntax error (#14106) --- documentation/docs/07-misc/02-testing.md | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/docs/07-misc/02-testing.md b/documentation/docs/07-misc/02-testing.md index 7609242471..1c3d4ba6e1 100644 --- a/documentation/docs/07-misc/02-testing.md +++ b/documentation/docs/07-misc/02-testing.md @@ -29,6 +29,7 @@ export default defineConfig({ conditions: ['browser'] } : undefined +}); ``` > [!NOTE] If loading the browser version of all your packages is undesirable, because (for example) you also test backend libraries, [you may need to resort to an alias configuration](https://github.com/testing-library/svelte-testing-library/issues/222#issuecomment-1909993331) From 96c299afc6ecc1493ad8683e55bf6feff75ebea5 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 1 Nov 2024 13:48:29 -0400 Subject: [PATCH 096/404] differentiate between 'effect' and `$effect` (#14105) --- documentation/docs/02-runes/04-$effect.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/docs/02-runes/04-$effect.md b/documentation/docs/02-runes/04-$effect.md index 3515a6c4e4..06e2fa140a 100644 --- a/documentation/docs/02-runes/04-$effect.md +++ b/documentation/docs/02-runes/04-$effect.md @@ -8,7 +8,7 @@ Most of the effects in a Svelte app are created by Svelte itself — they're the 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 `` element, or something across a network) with state inside your Svelte app. -> [!NOTE] Avoid overusing effects! When you do too much work in them, code often becomes difficult to understand and maintain. See [when not to use effects](#When-not-to-use-effects) to learn about alternative approaches. +> [!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=)): @@ -261,7 +261,7 @@ The `$effect.root` rune is an advanced feature that creates a non-tracked scope ``` -## When not to use effects +## When not to use `$effect` In general, `$effect` is best considered something of an escape hatch — useful for things like analytics and direct DOM manipulation — rather than a tool you should use frequently. In particular, avoid using it to synchronise state. Instead of this... From 4ec9986cba11d4635155e90c87c7ac5e6503a6c1 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Fri, 1 Nov 2024 19:09:27 +0100 Subject: [PATCH 097/404] fix: check options namespace for top level `svelte:element`s (#14101) We were checking it for nested elements, but not root elements fixes #13875 --- .changeset/polite-timers-tell.md | 5 +++++ .../phases/2-analyze/visitors/SvelteElement.js | 5 +++-- .../dynamic-element-svg-options-namespace/_config.js | 12 ++++++++++++ .../main.svelte | 7 +++++++ .../rect.svelte | 7 +++++++ 5 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 .changeset/polite-timers-tell.md create mode 100644 packages/svelte/tests/runtime-legacy/samples/dynamic-element-svg-options-namespace/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/dynamic-element-svg-options-namespace/main.svelte create mode 100644 packages/svelte/tests/runtime-legacy/samples/dynamic-element-svg-options-namespace/rect.svelte diff --git a/.changeset/polite-timers-tell.md b/.changeset/polite-timers-tell.md new file mode 100644 index 0000000000..b98a89c95f --- /dev/null +++ b/.changeset/polite-timers-tell.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: check options namespace for top level `svelte:element`s diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteElement.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteElement.js index 02717bdfd9..c0b9f95fc2 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteElement.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteElement.js @@ -35,9 +35,10 @@ export function SvelteElement(node, context) { ancestor.type === 'Component' || ancestor.type === 'SvelteComponent' || ancestor.type === 'SvelteFragment' || - ancestor.type === 'SnippetBlock' + ancestor.type === 'SnippetBlock' || + i === 0 ) { - // Inside a slot or a snippet -> this resets the namespace, so assume the component namespace + // Root element, or inside a slot or a snippet -> this resets the namespace, so assume the component namespace node.metadata.svg = context.state.options.namespace === 'svg'; node.metadata.mathml = context.state.options.namespace === 'mathml'; break; diff --git a/packages/svelte/tests/runtime-legacy/samples/dynamic-element-svg-options-namespace/_config.js b/packages/svelte/tests/runtime-legacy/samples/dynamic-element-svg-options-namespace/_config.js new file mode 100644 index 0000000000..a18ad5dc10 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/dynamic-element-svg-options-namespace/_config.js @@ -0,0 +1,12 @@ +import { test } from '../../test'; + +export default test({ + html: '', + + test({ assert, target }) { + const svg = target.querySelector('svg'); + const rect = target.querySelector('rect'); + assert.equal(svg?.namespaceURI, 'http://www.w3.org/2000/svg'); + assert.equal(rect?.namespaceURI, 'http://www.w3.org/2000/svg'); + } +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/dynamic-element-svg-options-namespace/main.svelte b/packages/svelte/tests/runtime-legacy/samples/dynamic-element-svg-options-namespace/main.svelte new file mode 100644 index 0000000000..0cc8a2a1f3 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/dynamic-element-svg-options-namespace/main.svelte @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/svelte/tests/runtime-legacy/samples/dynamic-element-svg-options-namespace/rect.svelte b/packages/svelte/tests/runtime-legacy/samples/dynamic-element-svg-options-namespace/rect.svelte new file mode 100644 index 0000000000..e747291714 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/dynamic-element-svg-options-namespace/rect.svelte @@ -0,0 +1,7 @@ + + + + + From 304d88edd0a0c8f89603aa3309217b46720ed304 Mon Sep 17 00:00:00 2001 From: "Ahmad S." Date: Fri, 1 Nov 2024 22:09:00 +0200 Subject: [PATCH 098/404] docs: fix `$effect` section examples (#13911) closes #13912 --- documentation/docs/02-runes/04-$effect.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/docs/02-runes/04-$effect.md b/documentation/docs/02-runes/04-$effect.md index 06e2fa140a..b338795220 100644 --- a/documentation/docs/02-runes/04-$effect.md +++ b/documentation/docs/02-runes/04-$effect.md @@ -288,7 +288,7 @@ 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`. -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#H4sIAAAAAAAACpVRy2rDMBD8lWXJwYE0dg-9KFYg31H3oNirIJBlYa1DjPG_F8l1XEop9LgzOzP7mFAbSwHF-4ROtYQCL97jAXn0sQh3skx4wNANfR2RMtS98XyuXMWWGLhjZUHCa1GcVix4cgwSdoEVU1bsn4wl_Y1I2kS6inekNdWcZXuQZ5giFDWpfwl5WYyT2fynbB1g1UWbTVbm2w6utOpKNq1TGucHhri6rLBX7kYVwtW4RtyVHUhOyXeGVj3klLxnyJP0i8lXNJUx6en-v6A48K85kTimpi0sYj-yAo-Wlh9FcL1LY4K3ahSgLT1OC3ZTXkBxfKN2uVC6T5LjAduuMdpQg4L7geaP-RNHPuClMQIAAA==)): +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 ``` From 4038b0cad67f8eebf652cee04e5c2691bcc40e89 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 2 Nov 2024 22:19:48 -0400 Subject: [PATCH 100/404] chore: setup docs previews (#14128) * chore: setup docs previews * test * delete --- .../workflows/docs-preview-create-request.yml | 26 ++++++++++++++++++ .../workflows/docs-preview-delete-request.yml | 27 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 .github/workflows/docs-preview-create-request.yml create mode 100644 .github/workflows/docs-preview-delete-request.yml diff --git a/.github/workflows/docs-preview-create-request.yml b/.github/workflows/docs-preview-create-request.yml new file mode 100644 index 0000000000..95ea820928 --- /dev/null +++ b/.github/workflows/docs-preview-create-request.yml @@ -0,0 +1,26 @@ +# https://github.com/sveltejs/svelte.dev/blob/main/apps/svelte.dev/scripts/sync-docs/README.md +name: Docs preview create request + +on: + pull_request: + 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 new file mode 100644 index 0000000000..d14704107d --- /dev/null +++ b/.github/workflows/docs-preview-delete-request.yml @@ -0,0 +1,27 @@ +# https://github.com/sveltejs/svelte.dev/blob/main/apps/svelte.dev/scripts/sync-docs/README.md +name: Docs preview delete request + +on: + pull_request: + 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 }} + } From 00f5b14d4029760b6e0583d5d4c0ebc7b3484520 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Sun, 3 Nov 2024 03:23:32 +0100 Subject: [PATCH 101/404] docs: fix description (#14109) Since Svelte 5, comments are also preserved on the client --- packages/svelte/src/compiler/types/index.d.ts | 2 +- packages/svelte/types/index.d.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/svelte/src/compiler/types/index.d.ts b/packages/svelte/src/compiler/types/index.d.ts index 40611b31ae..8a45f2d4fd 100644 --- a/packages/svelte/src/compiler/types/index.d.ts +++ b/packages/svelte/src/compiler/types/index.d.ts @@ -110,7 +110,7 @@ export interface CompileOptions extends ModuleCompileOptions { */ cssHash?: CssHashGetter; /** - * If `true`, your HTML comments will be preserved during server-side rendering. By default, they are stripped out. + * If `true`, your HTML comments will be preserved in the output. By default, they are stripped out. * * @default false */ diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 0f08fabfcb..ca67923ee1 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -812,7 +812,7 @@ declare module 'svelte/compiler' { */ cssHash?: CssHashGetter; /** - * If `true`, your HTML comments will be preserved during server-side rendering. By default, they are stripped out. + * If `true`, your HTML comments will be preserved in the output. By default, they are stripped out. * * @default false */ @@ -2183,7 +2183,7 @@ declare module 'svelte/types/compiler/interfaces' { */ cssHash?: CssHashGetter; /** - * If `true`, your HTML comments will be preserved during server-side rendering. By default, they are stripped out. + * If `true`, your HTML comments will be preserved in the output. By default, they are stripped out. * * @default false */ From a952860232203cbb3228387f5a3e70de28192796 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Sun, 3 Nov 2024 03:24:10 +0100 Subject: [PATCH 102/404] docs: more docs on TS types (#14065) * docs: more docs on TS types and a few related changes/enhancements closes #13940 * Apply suggestions from code review * adjust * Apply suggestions from code review * Update documentation/docs/07-misc/03-typescript.md * Update documentation/docs/07-misc/03-typescript.md * Update documentation/docs/07-misc/03-typescript.md --------- Co-authored-by: Rich Harris --- documentation/docs/07-misc/03-typescript.md | 17 ++++++++++++++++- .../docs/07-misc/07-v5-migration-guide.md | 5 +++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/documentation/docs/07-misc/03-typescript.md b/documentation/docs/07-misc/03-typescript.md index 525ba51d2f..5e16382187 100644 --- a/documentation/docs/07-misc/03-typescript.md +++ b/documentation/docs/07-misc/03-typescript.md @@ -195,7 +195,9 @@ Using it together with dynamic components to restrict what kinds of component ca ``` -Closely related to the `Component` type is the `ComponentProps` type which extracts the properties a component expects. +> [!LEGACY] In Svelte 4, components were of type `SvelteComponent` + +To extract the properties from a component, use `ComponentProps`. ```ts import type { Component, ComponentProps } from 'svelte'; @@ -211,6 +213,19 @@ function withProps>( withProps(MyComponent, { foo: 'bar' }); ``` +To declare that a variable expects the constructor or instance type of a component: + +```svelte + + + +``` + ## Enhancing built-in DOM types Svelte provides a best effort of all the HTML DOM types that exist. Sometimes you may want to use experimental attributes or custom events coming from an action. In these cases, TypeScript will throw a type error, saying that it does not know these types. If it's a non-experimental standard attribute/event, this may very well be a missing typing from our [HTML typings](https://github.com/sveltejs/svelte/blob/main/packages/svelte/elements.d.ts). In that case, you are welcome to open an issue and/or a PR fixing it. diff --git a/documentation/docs/07-misc/07-v5-migration-guide.md b/documentation/docs/07-misc/07-v5-migration-guide.md index 093ec5b86b..c7cdd36106 100644 --- a/documentation/docs/07-misc/07-v5-migration-guide.md +++ b/documentation/docs/07-misc/07-v5-migration-guide.md @@ -599,13 +599,14 @@ To declare that a component of a certain type is required: ```svelte From 74a2c6b5807de1540f48db3875dab36d56ca738e Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Sun, 3 Nov 2024 14:31:54 +0100 Subject: [PATCH 103/404] fix: migrate multiple declarations with only some exported correctly (#14126) --- .changeset/yellow-pumas-rule.md | 5 +++ packages/svelte/src/compiler/migrate/index.js | 41 +++++++++++++++---- .../input.svelte | 6 +++ .../output.svelte | 12 ++++++ 4 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 .changeset/yellow-pumas-rule.md create mode 100644 packages/svelte/tests/migrate/samples/export-props-multiple-declarations/input.svelte create mode 100644 packages/svelte/tests/migrate/samples/export-props-multiple-declarations/output.svelte diff --git a/.changeset/yellow-pumas-rule.md b/.changeset/yellow-pumas-rule.md new file mode 100644 index 0000000000..02def3c178 --- /dev/null +++ b/.changeset/yellow-pumas-rule.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: migrate multiple declarations with only some exported correctly diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index ee5abc8853..011d28d61a 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -505,7 +505,8 @@ const instance_script = { let nr_of_props = 0; - for (const declarator of node.declarations) { + for (let i = 0; i < node.declarations.length; i++) { + const declarator = node.declarations[i]; if (state.analysis.runes) { if (get_rune(declarator.init, state.scope) === '$props') { state.props_insertion_point = /** @type {number} */ (declarator.id.start) + 1; @@ -605,12 +606,38 @@ const instance_script = { }); } - state.props_insertion_point = /** @type {number} */ (declarator.end); - state.str.update( - /** @type {number} */ (declarator.start), - /** @type {number} */ (declarator.end), - '' - ); + let start = /** @type {number} */ (declarator.start); + let end = /** @type {number} */ (declarator.end); + + // handle cases like let a,b,c; where only some are exported + if (node.declarations.length > 1) { + // move the insertion point after the node itself; + state.props_insertion_point = /** @type {number} */ (node.end); + // if it's not the first declaration remove from the , of the previous declaration + if (i !== 0) { + start = state.str.original.indexOf( + ',', + /** @type {number} */ (node.declarations[i - 1].end) + ); + } + // if it's not the last declaration remove either from up until the + // start of the next declaration (if it's the first declaration) or + // up until the last index of , from the next declaration + if (i !== node.declarations.length - 1) { + if (i === 0) { + end = /** @type {number} */ (node.declarations[i + 1].start); + } else { + end = state.str.original.lastIndexOf( + ',', + /** @type {number} */ (node.declarations[i + 1].start) + ); + } + } + } else { + state.props_insertion_point = /** @type {number} */ (declarator.end); + } + + state.str.update(start, end, ''); continue; } diff --git a/packages/svelte/tests/migrate/samples/export-props-multiple-declarations/input.svelte b/packages/svelte/tests/migrate/samples/export-props-multiple-declarations/input.svelte new file mode 100644 index 0000000000..198bd44fc0 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/export-props-multiple-declarations/input.svelte @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/export-props-multiple-declarations/output.svelte b/packages/svelte/tests/migrate/samples/export-props-multiple-declarations/output.svelte new file mode 100644 index 0000000000..bb16ea2ff9 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/export-props-multiple-declarations/output.svelte @@ -0,0 +1,12 @@ + \ No newline at end of file From 8de5605b6a5ffa436d6fb9be5991345477e09f73 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Sun, 3 Nov 2024 14:36:11 +0100 Subject: [PATCH 104/404] fix: prevent migrated snippet from shadow snippet prop (#14127) --- .changeset/silent-tigers-lay.md | 5 +++ packages/svelte/src/compiler/migrate/index.js | 44 +++++++++++++++++++ .../shadowed-forwarded-slot/input.svelte | 15 +++++++ .../shadowed-forwarded-slot/output.svelte | 33 ++++++++++++++ 4 files changed, 97 insertions(+) create mode 100644 .changeset/silent-tigers-lay.md create mode 100644 packages/svelte/tests/migrate/samples/shadowed-forwarded-slot/input.svelte create mode 100644 packages/svelte/tests/migrate/samples/shadowed-forwarded-slot/output.svelte diff --git a/.changeset/silent-tigers-lay.md b/.changeset/silent-tigers-lay.md new file mode 100644 index 0000000000..8debfd7151 --- /dev/null +++ b/.changeset/silent-tigers-lay.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: prevent migrated snippet from shadow snippet prop diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index 011d28d61a..3513098a20 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -168,6 +168,7 @@ export function migrate(source, { filename, use_ts } = {}) { legacy_imports: new Set(), script_insertions: new Set(), derived_components: new Map(), + derived_conflicting_slots: new Map(), derived_labeled_statements: new Set(), has_svelte_self: false, uses_ts: @@ -199,6 +200,7 @@ export function migrate(source, { filename, use_ts } = {}) { const need_script = state.legacy_imports.size > 0 || state.derived_components.size > 0 || + state.derived_conflicting_slots.size > 0 || state.script_insertions.size > 0 || state.props.length > 0 || analysis.uses_rest_props || @@ -365,6 +367,13 @@ export function migrate(source, { filename, use_ts } = {}) { ); } + if (state.derived_conflicting_slots.size > 0) { + str.appendRight( + insertion_point, + `\n${indent}${[...state.derived_conflicting_slots.entries()].map(([name, init]) => `const ${name} = $derived(${init});`).join(`\n${indent}`)}\n` + ); + } + if (state.props.length > 0 && state.analysis.accessors) { str.appendRight( insertion_point, @@ -414,6 +423,7 @@ export function migrate(source, { filename, use_ts } = {}) { * legacy_imports: Set; * script_insertions: Set; * derived_components: Map; + * derived_conflicting_slots: Map; * derived_labeled_statements: Set; * has_svelte_self: boolean; * uses_ts: boolean; @@ -1133,6 +1143,7 @@ const template = { let name = 'children'; let slot_name = 'default'; let slot_props = '{ '; + let aliased_slot_name; for (const attr of node.attributes) { if (attr.type === 'SpreadAttribute') { @@ -1144,6 +1155,37 @@ const template = { if (attr.name === 'name') { slot_name = /** @type {any} */ (attr.value)[0].data; + // if some of the parents or this node itself har a slot + // attribute with the sane name of this slot + // we want to create a derived or the migrated snippet + // will shadow the slot prop + if ( + path.some( + (parent) => + (parent.type === 'RegularElement' || + parent.type === 'SvelteElement' || + parent.type === 'Component' || + parent.type === 'SvelteComponent' || + parent.type === 'SvelteFragment') && + parent.attributes.some( + (attribute) => + attribute.type === 'Attribute' && + attribute.name === 'slot' && + is_text_attribute(attribute) && + attribute.value[0].data === slot_name + ) + ) || + node.attributes.some( + (attribute) => + attribute.type === 'Attribute' && + attribute.name === 'slot' && + is_text_attribute(attribute) && + attribute.value[0].data === slot_name + ) + ) { + aliased_slot_name = `${slot_name}_render`; + state.derived_conflicting_slots.set(aliased_slot_name, slot_name); + } } else { const attr_value = attr.value === true || Array.isArray(attr.value) ? attr.value : [attr.value]; @@ -1200,6 +1242,8 @@ const template = { existing_prop.needs_refine_type = false; } + name = aliased_slot_name ?? name; + if (node.fragment.nodes.length > 0) { next(); state.str.update( diff --git a/packages/svelte/tests/migrate/samples/shadowed-forwarded-slot/input.svelte b/packages/svelte/tests/migrate/samples/shadowed-forwarded-slot/input.svelte new file mode 100644 index 0000000000..800ef2532c --- /dev/null +++ b/packages/svelte/tests/migrate/samples/shadowed-forwarded-slot/input.svelte @@ -0,0 +1,15 @@ +{#if $$slots.label} + +{/if} + + + + +
+ +
+ +
+
+
+
\ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/shadowed-forwarded-slot/output.svelte b/packages/svelte/tests/migrate/samples/shadowed-forwarded-slot/output.svelte new file mode 100644 index 0000000000..3c763349c6 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/shadowed-forwarded-slot/output.svelte @@ -0,0 +1,33 @@ + + +{#if label} + {@render label?.()} +{/if} + + {#snippet label()} + {@render label_render?.()} + {/snippet} + + + {#snippet label()} +
+ + {#snippet label()} +
+ {@render label_render?.()} +
+ {/snippet} +
+
+ {/snippet} +
\ No newline at end of file From 551284ca2259d934d34b71a38071d85684bd6d4f Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Sun, 3 Nov 2024 15:04:52 +0100 Subject: [PATCH 105/404] fix: correctly infer `` tag namespace (#14134) `` tags are valid in both the SVG and HTML namespace. If there's no parent, we therefore have to look downwards to see if it's the parent of a SVG or HTML element. fixes #7807 fixes #13793 --- .changeset/modern-pets-punch.md | 5 ++++ .../src/compiler/phases/2-analyze/types.d.ts | 4 +++ .../2-analyze/visitors/RegularElement.js | 13 ++++++++++ .../2-analyze/visitors/shared/component.js | 3 ++- .../html-namespace-infer-a-tag/_config.js | 26 +++++++++++++++++++ .../html-namespace-infer-a-tag/div.svelte | 7 +++++ .../html-namespace-infer-a-tag/main.svelte | 24 +++++++++++++++++ .../svg-namespace-infer-a-tag/_config.js | 26 +++++++++++++++++++ .../svg-namespace-infer-a-tag/main.svelte | 24 +++++++++++++++++ .../svg-namespace-infer-a-tag/svg.svelte | 7 +++++ 10 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 .changeset/modern-pets-punch.md create mode 100644 packages/svelte/tests/runtime-runes/samples/html-namespace-infer-a-tag/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/html-namespace-infer-a-tag/div.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/html-namespace-infer-a-tag/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/svg-namespace-infer-a-tag/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/svg-namespace-infer-a-tag/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/svg-namespace-infer-a-tag/svg.svelte diff --git a/.changeset/modern-pets-punch.md b/.changeset/modern-pets-punch.md new file mode 100644 index 0000000000..981547e9e5 --- /dev/null +++ b/.changeset/modern-pets-punch.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: correctly infer `` tag namespace 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 265f58130d..dedbe95ace 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/types.d.ts +++ b/packages/svelte/src/compiler/phases/2-analyze/types.d.ts @@ -9,6 +9,10 @@ export interface AnalysisState { analysis: ComponentAnalysis; options: ValidatedCompileOptions; ast_type: 'instance' | 'template' | 'module'; + /** + * Tag name of the parent element. `null` if the parent is `svelte:element`, `#snippet`, a component or the root. + * Parent doesn't necessarily mean direct path predecessor because there could be `#each`, `#if` etc in-between. + */ parent_element: string | null; has_props_rune: boolean; /** Which slots the current parent component has */ 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 98bb1ca92a..60bd1dd0c5 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js @@ -174,4 +174,17 @@ export function RegularElement(node, context) { } context.next({ ...context.state, parent_element: node.name }); + + // Special case: tags are valid in both the SVG and HTML namespace. + // If there's no parent, look downwards to see if it's the parent of a SVG or HTML element. + if (node.name === 'a' && !context.state.parent_element) { + for (const child of node.fragment.nodes) { + if (child.type === 'RegularElement') { + if (child.metadata.svg && child.name !== 'svg') { + node.metadata.svg = true; + break; + } + } + } + } } 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 73b051a848..9a432341a0 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,5 +1,5 @@ /** @import { AST } from '#compiler' */ -/** @import { Context } from '../../types' */ +/** @import { AnalysisState, Context } from '../../types' */ import * as e from '../../../../errors.js'; import { get_attribute_expression, is_expression_attribute } from '../../../../utils/ast.js'; import { determine_slot } from '../../../../utils/slot.js'; @@ -96,6 +96,7 @@ export function visit_component(node, context) { const component_slots = new Set(); for (const slot_name in nodes) { + /** @type {AnalysisState} */ const state = { ...context.state, scope: node.metadata.scopes[slot_name], diff --git a/packages/svelte/tests/runtime-runes/samples/html-namespace-infer-a-tag/_config.js b/packages/svelte/tests/runtime-runes/samples/html-namespace-infer-a-tag/_config.js new file mode 100644 index 0000000000..982e3a3d18 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/html-namespace-infer-a-tag/_config.js @@ -0,0 +1,26 @@ +import { test } from '../../test'; + +export default test({ + html: ` + + + +`, + test({ assert, target }) { + const div = target.querySelectorAll('div'); + const a = target.querySelectorAll('a'); + const span = target.querySelectorAll('span'); + + for (const element of div) { + assert.equal(element.namespaceURI, 'http://www.w3.org/1999/xhtml'); + } + + for (const element of a) { + assert.equal(element.namespaceURI, 'http://www.w3.org/1999/xhtml'); + } + + for (const element of span) { + assert.equal(element.namespaceURI, 'http://www.w3.org/1999/xhtml'); + } + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/html-namespace-infer-a-tag/div.svelte b/packages/svelte/tests/runtime-runes/samples/html-namespace-infer-a-tag/div.svelte new file mode 100644 index 0000000000..d3d283c634 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/html-namespace-infer-a-tag/div.svelte @@ -0,0 +1,7 @@ + + +
+ {@render children()} +
diff --git a/packages/svelte/tests/runtime-runes/samples/html-namespace-infer-a-tag/main.svelte b/packages/svelte/tests/runtime-runes/samples/html-namespace-infer-a-tag/main.svelte new file mode 100644 index 0000000000..e5d9d78860 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/html-namespace-infer-a-tag/main.svelte @@ -0,0 +1,24 @@ + + + + +
+ {#snippet test()} + + Hello + + {/snippet} + {@render test()} +
+ + diff --git a/packages/svelte/tests/runtime-runes/samples/svg-namespace-infer-a-tag/_config.js b/packages/svelte/tests/runtime-runes/samples/svg-namespace-infer-a-tag/_config.js new file mode 100644 index 0000000000..86309a1c86 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/svg-namespace-infer-a-tag/_config.js @@ -0,0 +1,26 @@ +import { test } from '../../test'; + +export default test({ + html: ` + Hello + Hello + Hello +`, + test({ assert, target }) { + const svg = target.querySelectorAll('svg'); + const a = target.querySelectorAll('a'); + const text = target.querySelectorAll('text'); + + for (const element of svg) { + assert.equal(element.namespaceURI, 'http://www.w3.org/2000/svg'); + } + + for (const element of a) { + assert.equal(element.namespaceURI, 'http://www.w3.org/2000/svg'); + } + + for (const element of text) { + assert.equal(element.namespaceURI, 'http://www.w3.org/2000/svg'); + } + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/svg-namespace-infer-a-tag/main.svelte b/packages/svelte/tests/runtime-runes/samples/svg-namespace-infer-a-tag/main.svelte new file mode 100644 index 0000000000..6d3a658c36 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/svg-namespace-infer-a-tag/main.svelte @@ -0,0 +1,24 @@ + + + + + Hello + + + + + {#snippet test()} + + Hello + + {/snippet} + {@render test()} + + + + + Hello + + diff --git a/packages/svelte/tests/runtime-runes/samples/svg-namespace-infer-a-tag/svg.svelte b/packages/svelte/tests/runtime-runes/samples/svg-namespace-infer-a-tag/svg.svelte new file mode 100644 index 0000000000..b5ca463293 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/svg-namespace-infer-a-tag/svg.svelte @@ -0,0 +1,7 @@ + + + + {@render children()} + From 8731f4f780da8c439e6b1abfcce579a3c07efb48 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 4 Nov 2024 17:32:50 +0100 Subject: [PATCH 106/404] docs: note what the value of `this` can be on `svelte:element` (#14117) closes #7575 --- documentation/docs/05-special-elements/05-svelte-element.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/documentation/docs/05-special-elements/05-svelte-element.md b/documentation/docs/05-special-elements/05-svelte-element.md index 17ce06799c..ef7dc32066 100644 --- a/documentation/docs/05-special-elements/05-svelte-element.md +++ b/documentation/docs/05-special-elements/05-svelte-element.md @@ -29,3 +29,5 @@ Svelte tries its best to infer the correct namespace from the element's surround ```svelte ``` + +`this` needs to be a valid DOM element tag, things like `#text` or `svelte:head` will not work. From c6904e892f9560f97573d4528cdcb6ea2f209f32 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 4 Nov 2024 15:08:15 -0500 Subject: [PATCH 107/404] use pull_request_target (#14148) --- .github/workflows/docs-preview-create-request.yml | 2 +- .github/workflows/docs-preview-delete-request.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs-preview-create-request.yml b/.github/workflows/docs-preview-create-request.yml index 95ea820928..f57766dc36 100644 --- a/.github/workflows/docs-preview-create-request.yml +++ b/.github/workflows/docs-preview-create-request.yml @@ -2,7 +2,7 @@ name: Docs preview create request on: - pull_request: + pull_request_target: branches: - main diff --git a/.github/workflows/docs-preview-delete-request.yml b/.github/workflows/docs-preview-delete-request.yml index d14704107d..4eb0e996a6 100644 --- a/.github/workflows/docs-preview-delete-request.yml +++ b/.github/workflows/docs-preview-delete-request.yml @@ -2,7 +2,7 @@ name: Docs preview delete request on: - pull_request: + pull_request_target: branches: - main types: [closed] From 87d83ffa39802ec0d6a798f399ab6790f6c19cd6 Mon Sep 17 00:00:00 2001 From: Christoph Sanz <22081130+christophsanz@users.noreply.github.com> Date: Mon, 4 Nov 2024 22:09:09 +0100 Subject: [PATCH 108/404] Add same note as in tutorial to doc. (#14136) Co-authored-by: Rich Harris --- documentation/docs/03-template-syntax/11-bind.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/documentation/docs/03-template-syntax/11-bind.md b/documentation/docs/03-template-syntax/11-bind.md index 4fe3e7c4a8..7dd03a6b04 100644 --- a/documentation/docs/03-template-syntax/11-bind.md +++ b/documentation/docs/03-template-syntax/11-bind.md @@ -223,6 +223,8 @@ 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`. + ## bind:this ```svelte From 57e27ae90e19df103a01a9a56117bf2402bb029a Mon Sep 17 00:00:00 2001 From: wackbyte Date: Mon, 4 Nov 2024 16:19:53 -0500 Subject: [PATCH 109/404] fix: only output the key for each_key_duplicate (#14147) Fixes #14146 --- .changeset/short-lions-search.md | 5 +++++ packages/svelte/src/internal/client/validate.js | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/short-lions-search.md diff --git a/.changeset/short-lions-search.md b/.changeset/short-lions-search.md new file mode 100644 index 0000000000..ed3abdc053 --- /dev/null +++ b/.changeset/short-lions-search.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: only output the key for each_key_duplicate diff --git a/packages/svelte/src/internal/client/validate.js b/packages/svelte/src/internal/client/validate.js index c3b1821947..eddcb69d9e 100644 --- a/packages/svelte/src/internal/client/validate.js +++ b/packages/svelte/src/internal/client/validate.js @@ -37,7 +37,7 @@ export function validate_each_keys(collection, key_fn) { const b = String(i); /** @type {string | null} */ - let k = String(array[i]); + let k = String(key); if (k.startsWith('[object ')) k = null; e.each_key_duplicate(a, b, k); From 7dbe812fc92ad87e30a91ddb3270261964531c54 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 4 Nov 2024 22:49:54 +0100 Subject: [PATCH 110/404] fix: ensure non-matching elements are scoped for `:not(...)` selector (#13999) If the contents of a `:not` selector don't match, then it's actually a match for `:not` because it's inverted. Therefore, we need to scope such elements. We're also making sure that contents of `:not` that never match actually count as a used (because the result is negated), and as such the contents of `:not` that always match are actually marked as unused. Fixes #13974 --- .changeset/dirty-parrots-smell.md | 5 ++ .../phases/2-analyze/css/css-prune.js | 89 ++++++++++++------- .../samples/not-selector-global/_config.js | 20 +++++ .../samples/not-selector-global/expected.css | 19 ++++ .../samples/not-selector-global/expected.html | 1 + .../samples/not-selector-global/input.svelte | 23 +++++ .../_config.js | 5 ++ .../expected.css | 4 + .../expected.html | 1 + .../input.svelte | 8 ++ .../tests/css/samples/not-selector/_config.js | 46 ++++------ .../css/samples/not-selector/expected.css | 21 ++--- .../css/samples/not-selector/expected.html | 1 + .../css/samples/not-selector/input.svelte | 19 ++-- 14 files changed, 172 insertions(+), 90 deletions(-) create mode 100644 .changeset/dirty-parrots-smell.md create mode 100644 packages/svelte/tests/css/samples/not-selector-global/_config.js create mode 100644 packages/svelte/tests/css/samples/not-selector-global/expected.css create mode 100644 packages/svelte/tests/css/samples/not-selector-global/expected.html create mode 100644 packages/svelte/tests/css/samples/not-selector-global/input.svelte create mode 100644 packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/_config.js create mode 100644 packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/expected.css create mode 100644 packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/expected.html create mode 100644 packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/input.svelte create mode 100644 packages/svelte/tests/css/samples/not-selector/expected.html diff --git a/.changeset/dirty-parrots-smell.md b/.changeset/dirty-parrots-smell.md new file mode 100644 index 0000000000..50864d20a4 --- /dev/null +++ b/.changeset/dirty-parrots-smell.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure non-matching elements are scoped for `:not(...)` 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 f71a860958..fae89f11ed 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 @@ -10,6 +10,7 @@ import { get_attribute_chunks, is_text_attribute } from '../../../utils/ast.js'; * stylesheet: Compiler.Css.StyleSheet; * element: Compiler.AST.RegularElement | Compiler.AST.SvelteElement; * from_render_tag: boolean; + * inside_not: boolean; * }} State */ /** @typedef {NODE_PROBABLY_EXISTS | NODE_DEFINITELY_EXISTS} NodeExistsValue */ @@ -61,9 +62,13 @@ export function prune(stylesheet, element) { const parent = get_element_parent(element); if (!parent) return; - walk(stylesheet, { stylesheet, element: parent, from_render_tag: true }, visitors); + walk( + stylesheet, + { stylesheet, element: parent, from_render_tag: true, inside_not: false }, + visitors + ); } else { - walk(stylesheet, { stylesheet, element, from_render_tag: false }, visitors); + walk(stylesheet, { stylesheet, element, from_render_tag: false, inside_not: false }, visitors); } } @@ -127,7 +132,7 @@ const visitors = { selectors_to_check, /** @type {Compiler.Css.Rule} */ (node.metadata.rule), element, - context.state.stylesheet + context.state ) ) { mark(inner, element); @@ -143,7 +148,7 @@ const visitors = { selectors, /** @type {Compiler.Css.Rule} */ (node.metadata.rule), context.state.element, - context.state.stylesheet + context.state ) ) { mark(inner, context.state.element); @@ -188,10 +193,10 @@ function truncate(node) { * @param {Compiler.Css.RelativeSelector[]} relative_selectors * @param {Compiler.Css.Rule} rule * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement} element - * @param {Compiler.Css.StyleSheet} stylesheet + * @param {State} state * @returns {boolean} */ -function apply_selector(relative_selectors, rule, element, stylesheet) { +function apply_selector(relative_selectors, rule, element, state) { const parent_selectors = relative_selectors.slice(); const relative_selector = parent_selectors.pop(); @@ -201,7 +206,7 @@ function apply_selector(relative_selectors, rule, element, stylesheet) { relative_selector, rule, element, - stylesheet + state ); if (!possible_match) { @@ -215,14 +220,14 @@ function apply_selector(relative_selectors, rule, element, stylesheet) { parent_selectors, rule, element, - stylesheet + state ); } // if this is the left-most non-global selector, mark it — we want // `x y z {...}` to become `x.blah y z.blah {...}` const parent = parent_selectors[parent_selectors.length - 1]; - if (!parent || is_global(parent, rule)) { + if (!state.inside_not && (!parent || is_global(parent, rule))) { mark(relative_selector, element); } @@ -235,17 +240,10 @@ function apply_selector(relative_selectors, rule, element, stylesheet) { * @param {Compiler.Css.RelativeSelector[]} parent_selectors * @param {Compiler.Css.Rule} rule * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement} element - * @param {Compiler.Css.StyleSheet} stylesheet + * @param {State} state * @returns {boolean} */ -function apply_combinator( - combinator, - relative_selector, - parent_selectors, - rule, - element, - stylesheet -) { +function apply_combinator(combinator, relative_selector, parent_selectors, rule, element, state) { const name = combinator.name; switch (name) { @@ -269,9 +267,9 @@ function apply_combinator( } if (parent.type === 'RegularElement' || parent.type === 'SvelteElement') { - if (apply_selector(parent_selectors, rule, parent, stylesheet)) { + if (apply_selector(parent_selectors, rule, parent, state)) { // TODO the `name === ' '` causes false positives, but removing it causes false negatives... - if (name === ' ' || crossed_component_boundary) { + if (!state.inside_not && (name === ' ' || crossed_component_boundary)) { mark(parent_selectors[parent_selectors.length - 1], parent); } @@ -297,11 +295,15 @@ function apply_combinator( if (possible_sibling.type === 'RenderTag' || possible_sibling.type === 'SlotElement') { // `{@render foo()}

foo

` with `:global(.x) + p` is a match if (parent_selectors.length === 1 && parent_selectors[0].metadata.is_global) { - mark(relative_selector, element); + if (!state.inside_not) { + mark(relative_selector, element); + } sibling_matched = true; } - } else if (apply_selector(parent_selectors, rule, possible_sibling, stylesheet)) { - mark(relative_selector, element); + } else if (apply_selector(parent_selectors, rule, possible_sibling, state)) { + if (!state.inside_not) { + mark(relative_selector, element); + } sibling_matched = true; } } @@ -381,10 +383,10 @@ const regex_backslash_and_following_character = /\\(.)/g; * @param {Compiler.Css.RelativeSelector} relative_selector * @param {Compiler.Css.Rule} rule * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement} element - * @param {Compiler.Css.StyleSheet} stylesheet + * @param {State} state * @returns {boolean } */ -function relative_selector_might_apply_to_node(relative_selector, rule, element, stylesheet) { +function relative_selector_might_apply_to_node(relative_selector, rule, element, state) { // Sort :has(...) selectors in one bucket and everything else into another const has_selectors = []; const other_selectors = []; @@ -458,7 +460,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element, if ( selectors.length === 0 /* is :global(...) */ || (element.metadata.scoped && selector_matched) || - apply_selector(selectors, rule, element, stylesheet) + apply_selector(selectors, rule, element, state) ) { complex_selector.metadata.used = true; selector_matched = matched = true; @@ -504,7 +506,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, stylesheet); + return apply_selector(complex_selector.children, rule, element, state); } // We came across a :global, everything beyond it is global and therefore a potential match @@ -515,12 +517,37 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element, for (const complex_selector of selector.args.children) { const relative = truncate(complex_selector); - if ( - relative.length === 0 /* is :global(...) */ || - apply_selector(relative, rule, element, stylesheet) + const is_global = relative.length === 0; + + if (is_global) { + complex_selector.metadata.used = true; + matched = true; + } else if (name !== 'not' && apply_selector(relative, rule, element, state)) { + complex_selector.metadata.used = true; + matched = true; + } else if ( + name === 'not' && + !apply_selector(relative, rule, element, { ...state, inside_not: true }) ) { + // For `:not(...)` we gotta do the inverse: If it did not match, mark the element and possibly + // everything above (if the selector is written is a such) as scoped (because they matched by not matching). + element.metadata.scoped = true; complex_selector.metadata.used = true; matched = true; + + for (const r of relative) { + r.metadata.scoped = true; + } + + // bar:not(foo bar) means that foo is an ancestor of bar + if (complex_selector.children.length > 1) { + /** @type {Compiler.AST.RegularElement | Compiler.AST.SvelteElement | null} */ + let el = get_element_parent(element); + while (el) { + el.metadata.scoped = true; + el = get_element_parent(el); + } + } } else if (complex_selector.children.length > 1 && (name == 'is' || name == 'where')) { // foo :is(bar baz) can also mean that bar is an ancestor of foo, and baz a descendant. // We can't fully check if that actually matches with our current algorithm, so we just assume it does. @@ -618,7 +645,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element, const parent = /** @type {Compiler.Css.Rule} */ (rule.metadata.parent_rule); for (const complex_selector of parent.prelude.children) { - if (apply_selector(truncate(complex_selector), parent, element, stylesheet)) { + if (apply_selector(truncate(complex_selector), parent, element, state)) { complex_selector.metadata.used = true; matched = true; } diff --git a/packages/svelte/tests/css/samples/not-selector-global/_config.js b/packages/svelte/tests/css/samples/not-selector-global/_config.js new file mode 100644 index 0000000000..2776ac227a --- /dev/null +++ b/packages/svelte/tests/css/samples/not-selector-global/_config.js @@ -0,0 +1,20 @@ +import { test } from '../../test'; + +export default test({ + warnings: [ + { + code: 'css_unused_selector', + message: 'Unused CSS selector ":global(.x) :not(p)"', + start: { + line: 14, + column: 1, + character: 197 + }, + end: { + line: 14, + column: 20, + character: 216 + } + } + ] +}); diff --git a/packages/svelte/tests/css/samples/not-selector-global/expected.css b/packages/svelte/tests/css/samples/not-selector-global/expected.css new file mode 100644 index 0000000000..f90edd0c88 --- /dev/null +++ b/packages/svelte/tests/css/samples/not-selector-global/expected.css @@ -0,0 +1,19 @@ + + .svelte-xyz:not(.foo) { + color: green; + } + .svelte-xyz:not(.foo:where(.svelte-xyz)):not(.unused) { + color: green; + } + .x:not(.foo.svelte-xyz) { + color: green; + } + /* (unused) :global(.x) :not(p) { + color: red; + }*/ + .x:not(p.svelte-xyz) { + color: red; /* TODO would be nice to prune this one day */ + } + .x .svelte-xyz:not(.unused:where(.svelte-xyz)) { + color: green; + } diff --git a/packages/svelte/tests/css/samples/not-selector-global/expected.html b/packages/svelte/tests/css/samples/not-selector-global/expected.html new file mode 100644 index 0000000000..b4834bb818 --- /dev/null +++ b/packages/svelte/tests/css/samples/not-selector-global/expected.html @@ -0,0 +1 @@ +

foo

bar

\ No newline at end of file diff --git a/packages/svelte/tests/css/samples/not-selector-global/input.svelte b/packages/svelte/tests/css/samples/not-selector-global/input.svelte new file mode 100644 index 0000000000..578052d943 --- /dev/null +++ b/packages/svelte/tests/css/samples/not-selector-global/input.svelte @@ -0,0 +1,23 @@ +

foo

+

bar

+ + \ No newline at end of file diff --git a/packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/_config.js b/packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/_config.js new file mode 100644 index 0000000000..292c6c49ac --- /dev/null +++ b/packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + warnings: [] +}); diff --git a/packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/expected.css b/packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/expected.css new file mode 100644 index 0000000000..8fe09de6dc --- /dev/null +++ b/packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/expected.css @@ -0,0 +1,4 @@ + + .svelte-xyz:not(.bar:where(.svelte-xyz)) { + color: green; + } diff --git a/packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/expected.html b/packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/expected.html new file mode 100644 index 0000000000..fa4ca95c60 --- /dev/null +++ b/packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/expected.html @@ -0,0 +1 @@ +

foo

bar

\ No newline at end of file diff --git a/packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/input.svelte b/packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/input.svelte new file mode 100644 index 0000000000..6599a21ca6 --- /dev/null +++ b/packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/input.svelte @@ -0,0 +1,8 @@ +

foo

+

bar

+ + \ No newline at end of file diff --git a/packages/svelte/tests/css/samples/not-selector/_config.js b/packages/svelte/tests/css/samples/not-selector/_config.js index 6ab49a8809..27b4f91a6a 100644 --- a/packages/svelte/tests/css/samples/not-selector/_config.js +++ b/packages/svelte/tests/css/samples/not-selector/_config.js @@ -4,58 +4,44 @@ export default test({ warnings: [ { code: 'css_unused_selector', - message: 'Unused CSS selector ":not(.unused)"', + message: 'Unused CSS selector ":not(p)"', start: { - line: 8, + line: 11, column: 1, - character: 89 + character: 125 }, end: { - line: 8, - column: 14, - character: 102 - } - }, - { - code: 'css_unused_selector', - message: 'Unused CSS selector ":not(.foo):not(.unused)"', - start: { - line: 18, - column: 1, - character: 221 - }, - end: { - line: 18, - column: 24, - character: 244 + line: 11, + column: 8, + character: 132 } }, { code: 'css_unused_selector', message: 'Unused CSS selector "p :not(.foo)"', start: { - line: 25, + line: 22, column: 1, - character: 300 + character: 235 }, end: { - line: 25, + line: 22, column: 13, - character: 312 + character: 247 } }, { code: 'css_unused_selector', - message: 'Unused CSS selector ":global(.x) :not(.unused)"', + message: 'Unused CSS selector "p :not(.unused)"', start: { - line: 34, + line: 25, column: 1, - character: 469 + character: 268 }, end: { - line: 34, - column: 26, - character: 494 + line: 25, + column: 16, + character: 283 } } ] diff --git a/packages/svelte/tests/css/samples/not-selector/expected.css b/packages/svelte/tests/css/samples/not-selector/expected.css index 45e9ad69c0..b4d51bf269 100644 --- a/packages/svelte/tests/css/samples/not-selector/expected.css +++ b/packages/svelte/tests/css/samples/not-selector/expected.css @@ -2,19 +2,16 @@ .svelte-xyz:not(.foo:where(.svelte-xyz)) { color: green; } - /* (unused) :not(.unused) { - color: red; - }*/ - .svelte-xyz:not(.foo) { + .svelte-xyz:not(.unused:where(.svelte-xyz)) { color: green; } + /* (unused) :not(p) { + color: red; + }*/ - .svelte-xyz:not(.foo:where(.svelte-xyz)):not(.unused) { + .svelte-xyz:not(.foo:where(.svelte-xyz)):not(.unused:where(.svelte-xyz)) { color: green; } - /* (unused) :not(.foo):not(.unused) { - color: red; - }*/ p.svelte-xyz:not(.foo:where(.svelte-xyz)) { color: green; @@ -22,12 +19,6 @@ /* (unused) p :not(.foo) { color: red; }*/ - .x:not(.foo.svelte-xyz) { - color: green; - } - .x:not(.unused.svelte-xyz) { - color: red; /* TODO would be nice to prune this one day */ - } - /* (unused) :global(.x) :not(.unused) { + /* (unused) p :not(.unused) { color: red; }*/ diff --git a/packages/svelte/tests/css/samples/not-selector/expected.html b/packages/svelte/tests/css/samples/not-selector/expected.html new file mode 100644 index 0000000000..b4834bb818 --- /dev/null +++ b/packages/svelte/tests/css/samples/not-selector/expected.html @@ -0,0 +1 @@ +

foo

bar

\ No newline at end of file diff --git a/packages/svelte/tests/css/samples/not-selector/input.svelte b/packages/svelte/tests/css/samples/not-selector/input.svelte index 57d062cf49..a0e72c7be8 100644 --- a/packages/svelte/tests/css/samples/not-selector/input.svelte +++ b/packages/svelte/tests/css/samples/not-selector/input.svelte @@ -6,17 +6,14 @@ color: green; } :not(.unused) { - color: red; - } - :not(:global(.foo)) { color: green; } - - :not(.foo):not(:global(.unused)) { - color: green; + :not(p) { + color: red; } + :not(.foo):not(.unused) { - color: red; + color: green; } p:not(.foo) { @@ -25,13 +22,7 @@ p :not(.foo) { color: red; } - :global(.x):not(.foo) { - color: green; - } - :global(.x):not(.unused) { - color: red; /* TODO would be nice to prune this one day */ - } - :global(.x) :not(.unused) { + p :not(.unused) { color: red; } \ No newline at end of file From e6dd871819a5da9a749e7e0f015a774be8eb5ee3 Mon Sep 17 00:00:00 2001 From: Gonzalo Ruiz Date: Mon, 4 Nov 2024 23:31:36 +0100 Subject: [PATCH 111/404] fix: ensure migrate keeps inline/trailing comments in $props type definition and produces non-broken code (#14143) * feat[sv migrate]: keep inline/trailing comments when migrating export let types to type definition * test: add tests for inline comment migration * chore: add changeset * fix: migrate trailing multiline comment parsing no longer results in broken code, FIXES PR #14143#issuecomment-2455702689 * test: add migrate test with same-line trailing multiline comments and same-line leading multiline comments * chore: add changeset * fix: lint --------- Co-authored-by: Gonzalo Ruiz Co-authored-by: paoloricciuti --- .changeset/mean-seas-give.md | 5 ++ .changeset/purple-owls-hug.md | 5 ++ packages/svelte/src/compiler/migrate/index.js | 72 +++++++++++++++++-- .../samples/jsdoc-with-comments/input.svelte | 17 ++++- .../samples/jsdoc-with-comments/output.svelte | 16 ++++- .../migrate/samples/props-ts/input.svelte | 10 +++ .../migrate/samples/props-ts/output.svelte | 20 +++++- 7 files changed, 134 insertions(+), 11 deletions(-) create mode 100644 .changeset/mean-seas-give.md create mode 100644 .changeset/purple-owls-hug.md diff --git a/.changeset/mean-seas-give.md b/.changeset/mean-seas-give.md new file mode 100644 index 0000000000..0c16ac6d55 --- /dev/null +++ b/.changeset/mean-seas-give.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure trailing multiline comments on props produce correct code (#14143#issuecomment-2455702689) diff --git a/.changeset/purple-owls-hug.md b/.changeset/purple-owls-hug.md new file mode 100644 index 0000000000..f3dcb0a5e3 --- /dev/null +++ b/.changeset/purple-owls-hug.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure migrate keeps inline/trailing comments in $props type definition diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index 3513098a20..8e8670c824 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -279,7 +279,7 @@ export function migrate(source, { filename, use_ts } = {}) { type = `interface ${type_name} {${newline_separator}${state.props .map((prop) => { const comment = prop.comment ? `${prop.comment}${newline_separator}` : ''; - return `${comment}${prop.exported}${prop.optional ? '?' : ''}: ${prop.type};`; + return `${comment}${prop.exported}${prop.optional ? '?' : ''}: ${prop.type};${prop.trailing_comment ? ' ' + prop.trailing_comment : ''}`; }) .join(newline_separator)}`; if (analysis.uses_props || analysis.uses_rest_props) { @@ -289,7 +289,7 @@ export function migrate(source, { filename, use_ts } = {}) { } else { type = `/**\n${indent} * @typedef {Object} ${type_name}${state.props .map((prop) => { - return `\n${indent} * @property {${prop.type}} ${prop.optional ? `[${prop.exported}]` : prop.exported}${prop.comment ? ` - ${prop.comment}` : ''}`; + return `\n${indent} * @property {${prop.type}} ${prop.optional ? `[${prop.exported}]` : prop.exported}${prop.comment ? ` - ${prop.comment}` : ''}${prop.trailing_comment ? ` - ${prop.trailing_comment.trim()}` : ''}`; }) .join(``)}\n${indent} */`; } @@ -414,7 +414,7 @@ export function migrate(source, { filename, use_ts } = {}) { * analysis: ComponentAnalysis; * filename?: string; * indent: string; - * props: Array<{ local: string; exported: string; init: string; bindable: boolean; slot_name?: string; optional: boolean; type: string; comment?: string; type_only?: boolean; needs_refine_type?: boolean; }>; + * props: Array<{ local: string; exported: string; init: string; bindable: boolean; slot_name?: string; optional: boolean; type: string; comment?: string; trailing_comment?: string; type_only?: boolean; needs_refine_type?: boolean; }>; * props_insertion_point: number; * has_props_rune: boolean; * has_type_or_fallback: boolean; @@ -1497,13 +1497,28 @@ function extract_type_and_comment(declarator, state, path) { str.update(comment_start, comment_end, ''); } + // Find trailing comments + const trailing_comment_node = /** @type {Node} */ (parent)?.trailingComments?.at(0); + const trailing_comment_start = /** @type {any} */ (trailing_comment_node)?.start; + const trailing_comment_end = /** @type {any} */ (trailing_comment_node)?.end; + let trailing_comment = + trailing_comment_node && str.original.substring(trailing_comment_start, trailing_comment_end); + + if (trailing_comment_node) { + str.update(trailing_comment_start, trailing_comment_end, ''); + } + if (declarator.id.typeAnnotation) { state.has_type_or_fallback = true; let start = declarator.id.typeAnnotation.start + 1; // skip the colon while (str.original[start] === ' ') { start++; } - return { type: str.original.substring(start, declarator.id.typeAnnotation.end), comment }; + return { + type: str.original.substring(start, declarator.id.typeAnnotation.end), + comment, + trailing_comment + }; } let cleaned_comment_arr = comment @@ -1526,12 +1541,43 @@ function extract_type_and_comment(declarator, state, path) { ?.slice(0, first_at_comment !== -1 ? first_at_comment : cleaned_comment_arr.length) .join('\n'); + let cleaned_comment_arr_trailing = trailing_comment + ?.split('\n') + .map((line) => + line + .trim() + // replace `// ` for one liners + .replace(/^\/\/\s*/g, '') + // replace `\**` for the initial JSDoc + .replace(/^\/\*\*?\s*/g, '') + // migrate `*/` for the end of JSDoc + .replace(/\s*\*\/$/g, '') + // remove any initial `* ` to clean the comment + .replace(/^\*\s*/g, '') + ) + .filter(Boolean); + const first_at_comment_trailing = cleaned_comment_arr_trailing?.findIndex((line) => + line.startsWith('@') + ); + let cleaned_comment_trailing = cleaned_comment_arr_trailing + ?.slice( + 0, + first_at_comment_trailing !== -1 + ? first_at_comment_trailing + : cleaned_comment_arr_trailing.length + ) + .join('\n'); + // try to find a comment with a type annotation, hinting at jsdoc if (parent?.type === 'ExportNamedDeclaration' && comment_node) { state.has_type_or_fallback = true; const match = /@type {(.+)}/.exec(comment_node.value); if (match) { - return { type: match[1], comment: cleaned_comment }; + return { + type: match[1], + comment: cleaned_comment, + trailing_comment: cleaned_comment_trailing + }; } } @@ -1540,11 +1586,19 @@ function extract_type_and_comment(declarator, state, path) { state.has_type_or_fallback = true; // only assume type if it's trivial to infer - else someone would've added a type annotation const type = typeof declarator.init.value; if (type === 'string' || type === 'number' || type === 'boolean') { - return { type, comment: state.uses_ts ? comment : cleaned_comment }; + return { + type, + comment: state.uses_ts ? comment : cleaned_comment, + trailing_comment: state.uses_ts ? trailing_comment : cleaned_comment_trailing + }; } } - return { type: 'any', comment: state.uses_ts ? comment : cleaned_comment }; + return { + type: 'any', + comment: state.uses_ts ? comment : cleaned_comment, + trailing_comment: state.uses_ts ? trailing_comment : cleaned_comment_trailing + }; } // Ensure modifiers are applied in the same order as Svelte 4 @@ -1779,10 +1833,13 @@ function handle_identifier(node, state, path) { comment = state.str.original.substring(comment_node.start, comment_node.end); } + const trailing_comment = member.trailingComments?.at(0)?.value; + if (prop) { prop.type = type; prop.optional = member.optional; prop.comment = comment ?? prop.comment; + prop.trailing_comment = trailing_comment ?? prop.trailing_comment; } else { state.props.push({ local: member.key.name, @@ -1792,6 +1849,7 @@ function handle_identifier(node, state, path) { optional: member.optional, type, comment, + trailing_comment, type_only: true }); } 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 333980513a..f2efb1db80 100644 --- a/packages/svelte/tests/migrate/samples/jsdoc-with-comments/input.svelte +++ b/packages/svelte/tests/migrate/samples/jsdoc-with-comments/input.svelte @@ -24,5 +24,20 @@ /** * This is optional */ - export let optional = {stuff: true}; + export let optional = {stuff: true}; + + export let inline_commented; // this should stay a comment + + /** + * This comment should be merged + */ + export let inline_commented_merged; // with this inline comment + + /* + * this is a same-line leading multiline comment + **/ export let inline_multiline_leading_comment = 'world'; + + export let inline_multiline_trailing_comment = 'world'; /* + * this is a same-line trailing multiline comment + **/ \ 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 271dd23ffb..19fbe38b50 100644 --- a/packages/svelte/tests/migrate/samples/jsdoc-with-comments/output.svelte +++ b/packages/svelte/tests/migrate/samples/jsdoc-with-comments/output.svelte @@ -9,6 +9,12 @@ + + + + + + /** * @typedef {Object} Props * @property {string} comment - My wonderful comment @@ -17,6 +23,10 @@ * @property {any} no_comment * @property {boolean} type_no_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 */ /** @type {Props} */ @@ -26,6 +36,10 @@ one_line, no_comment, type_no_comment, - optional = {stuff: true} + optional = {stuff: true}, + inline_commented, + inline_commented_merged, + inline_multiline_leading_comment = 'world', + inline_multiline_trailing_comment = 'world' } = $props(); \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/props-ts/input.svelte b/packages/svelte/tests/migrate/samples/props-ts/input.svelte index 34007c08b7..9378c54ecf 100644 --- a/packages/svelte/tests/migrate/samples/props-ts/input.svelte +++ b/packages/svelte/tests/migrate/samples/props-ts/input.svelte @@ -6,6 +6,16 @@ export let bindingOptional: string | undefined = 'bar'; /** this should stay a comment */ export let no_type_but_comment = 0; + export let type_and_inline_comment:number; // this should also stay a comment + export let no_type_and_inline_comment = 0; // this should stay as well + + /* + * this is a same-line leading multiline comment + **/ export let inline_multiline_leading_comment = 'world'; + + export let inline_multiline_trailing_comment = 'world'; /* + * this is a same-line trailing multiline comment + **/ {readonly} diff --git a/packages/svelte/tests/migrate/samples/props-ts/output.svelte b/packages/svelte/tests/migrate/samples/props-ts/output.svelte index 36a0b0f4fb..e9deea1384 100644 --- a/packages/svelte/tests/migrate/samples/props-ts/output.svelte +++ b/packages/svelte/tests/migrate/samples/props-ts/output.svelte @@ -1,6 +1,9 @@ {readonly} {optional} - \ No newline at end of file + From 3d67cd53dbee6079834e208de4f65861798a3131 Mon Sep 17 00:00:00 2001 From: Pig Fang Date: Tue, 5 Nov 2024 17:13:46 +0800 Subject: [PATCH 112/404] docs: fix migration guide typo (#14155) --- documentation/docs/07-misc/07-v5-migration-guide.md | 2 +- 1 file changed, 1 insertion(+), 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 c7cdd36106..4ebfb5f905 100644 --- a/documentation/docs/07-misc/07-v5-migration-guide.md +++ b/documentation/docs/07-misc/07-v5-migration-guide.md @@ -36,7 +36,7 @@ In Svelte 4, a `$:` statement at the top level of a component could be used to d ``` -As with `$state`, nothing else changes. `double` is still the number itself, and you read it directly, without a wrapper like `.value` or `getCount()`. +As with `$state`, nothing else changes. `double` is still the number itself, and you read it directly, without a wrapper like `.value` or `getDouble()`. A `$:` statement could also be used to create side effects. In Svelte 5, this is achieved using the `$effect` rune: From 0ce4b559f213944b426eb5cb687d5b22ba51bef7 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Tue, 5 Nov 2024 14:07:54 +0100 Subject: [PATCH 113/404] chore: add custom comment with link to playground for `pkg.pr.new` (#14151) * chore: add custom comment with link to playground for `pkg.pr.new` * chore: small updates to the script output * chore: refine comment a bit, include multiple packages * tweak comment * chore: two steps workflow * chore: update workflow --------- Co-authored-by: Rich Harris --- .github/workflows/pkg.pr.new-comment.yml | 107 +++++++++++++++++++++++ .github/workflows/pkg.pr.new.yml | 18 +++- 2 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/pkg.pr.new-comment.yml diff --git a/.github/workflows/pkg.pr.new-comment.yml b/.github/workflows/pkg.pr.new-comment.yml new file mode 100644 index 0000000000..b6ad1272f0 --- /dev/null +++ b/.github/workflows/pkg.pr.new-comment.yml @@ -0,0 +1,107 @@ +name: Update pkg.pr.new comment + +on: + workflow_run: + workflows: ['Publish Any Commit'] + types: + - completed + +jobs: + build: + name: 'Update comment' + runs-on: ubuntu-latest + steps: + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: output.json + path: output.json + github-token: ${{ secrets.GITHUB_TOKEN }} + run-id: ${{ github.event.workflow_run.id }} + - name: 'Post or update comment' + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const fs = require('fs'); + const output = JSON.parse(fs.readFileSync('output.json', 'utf8')); + + const bot_comment_identifier = ``; + + const body = (number) => `${bot_comment_identifier} + + [Playground](https://svelte.dev/playground?version=pr-${number}) + + \`\`\` + ${output.packages.map((p) => `pnpm add https://pkg.pr.new/${p.name}@${number}`).join('\n')} + \`\`\` + `; + + async function find_bot_comment(issue_number) { + if (!issue_number) return null; + const comments = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issue_number, + }); + return comments.data.find((comment) => + comment.body.includes(bot_comment_identifier) + ); + } + + async function create_or_update_comment(issue_number) { + if (!issue_number) { + console.log('No issue number provided. Cannot post or update comment.'); + return; + } + + const existing_comment = await find_bot_comment(issue_number); + if (existing_comment) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing_comment.id, + body: body(issue_number), + }); + } else { + await github.rest.issues.createComment({ + issue_number: issue_number, + owner: context.repo.owner, + repo: context.repo.repo, + body: body(issue_number), + }); + } + } + + async function log_publish_info() { + console.log('\n' + '='.repeat(50)); + console.log('Publish Information'); + console.log('='.repeat(50)); + console.log('\nPublished Packages:'); + console.log(packages); + console.log('\nPlayground URL:'); + console.log(`\nhttps://svelte.dev/playground?version=commit-${output.sha.substring(0, 7)}`) + console.log('\n' + '='.repeat(50)); + } + + if (output.event_name === 'pull_request') { + if (context.issue.number) { + await create_or_update_comment(context.issue.number); + } + } else if (output.event_name === 'push') { + const pull_requests = await github.rest.pulls.list({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open', + head: `${context.repo.owner}:${output.ref.replace('refs/heads/', '')}`, + }); + + if (pull_requests.data.length > 0) { + await create_or_update_comment(pull_requests.data[0].number); + } else { + console.log( + 'No open pull request found for this push. Logging publish information to console:' + ); + await log_publish_info(); + } + } diff --git a/.github/workflows/pkg.pr.new.yml b/.github/workflows/pkg.pr.new.yml index 76736782a0..ccb05ec93b 100644 --- a/.github/workflows/pkg.pr.new.yml +++ b/.github/workflows/pkg.pr.new.yml @@ -21,4 +21,20 @@ jobs: - name: Build run: pnpm build - - run: pnpx pkg-pr-new publish --compact --no-template './packages/svelte' + - run: pnpx pkg-pr-new publish --comment=off --json output.json --compact --no-template './packages/svelte' + - name: Add metadata to output + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const fs = require('fs'); + const output = JSON.parse(fs.readFileSync('output.json', 'utf8')); + output.number = context.issue.number; + output.event_name = context.eventName; + output.ref = context.ref; + fs.writeFileSync('output.json', JSON.stringify(output), 'utf8'); + - name: Upload output + uses: actions/upload-artifact@v4 + with: + name: output.json + path: output.json From f1f4c29a9980480eefe75b973272c894aceaad17 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Tue, 5 Nov 2024 15:03:23 +0100 Subject: [PATCH 114/404] =?UTF-8?q?fix:=20`pkg.pr.new`=C2=A0comment=20work?= =?UTF-8?q?flow=20(#14156)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: `pkg.pr.new` comment workflow * chore: remove path from initial workflow and add ls * chore: add path back since it's mandatory --- .github/workflows/pkg.pr.new-comment.yml | 7 ++++--- .github/workflows/pkg.pr.new.yml | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pkg.pr.new-comment.yml b/.github/workflows/pkg.pr.new-comment.yml index b6ad1272f0..10d979121f 100644 --- a/.github/workflows/pkg.pr.new-comment.yml +++ b/.github/workflows/pkg.pr.new-comment.yml @@ -15,9 +15,10 @@ jobs: uses: actions/download-artifact@v4 with: name: output.json - path: output.json github-token: ${{ secrets.GITHUB_TOKEN }} run-id: ${{ github.event.workflow_run.id }} + + - run: ls -R . - name: 'Post or update comment' uses: actions/github-script@v6 with: @@ -85,8 +86,8 @@ jobs: } if (output.event_name === 'pull_request') { - if (context.issue.number) { - await create_or_update_comment(context.issue.number); + if (output.number) { + await create_or_update_comment(output.number); } } else if (output.event_name === 'push') { const pull_requests = await github.rest.pulls.list({ diff --git a/.github/workflows/pkg.pr.new.yml b/.github/workflows/pkg.pr.new.yml index ccb05ec93b..a55dd5963c 100644 --- a/.github/workflows/pkg.pr.new.yml +++ b/.github/workflows/pkg.pr.new.yml @@ -37,4 +37,6 @@ jobs: uses: actions/upload-artifact@v4 with: name: output.json - path: output.json + path: ./output + + - run: ls -R . From 16a40a8bcea44ff2ea8a062d20bc6e109bf65a22 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Tue, 5 Nov 2024 15:30:04 +0100 Subject: [PATCH 115/404] fix: use both correct paths (#14157) * fix: try to read from right folder * fix: use both correct paths --- .github/workflows/pkg.pr.new-comment.yml | 2 +- .github/workflows/pkg.pr.new.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pkg.pr.new-comment.yml b/.github/workflows/pkg.pr.new-comment.yml index 10d979121f..65038c9a4c 100644 --- a/.github/workflows/pkg.pr.new-comment.yml +++ b/.github/workflows/pkg.pr.new-comment.yml @@ -14,7 +14,7 @@ jobs: - name: Download artifact uses: actions/download-artifact@v4 with: - name: output.json + name: output github-token: ${{ secrets.GITHUB_TOKEN }} run-id: ${{ github.event.workflow_run.id }} diff --git a/.github/workflows/pkg.pr.new.yml b/.github/workflows/pkg.pr.new.yml index a55dd5963c..4292ec900a 100644 --- a/.github/workflows/pkg.pr.new.yml +++ b/.github/workflows/pkg.pr.new.yml @@ -36,7 +36,7 @@ jobs: - name: Upload output uses: actions/upload-artifact@v4 with: - name: output.json - path: ./output + name: output + path: ./output.json - run: ls -R . From 0c853208ceba88053516312e27797df387d45c82 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Tue, 5 Nov 2024 15:46:01 +0100 Subject: [PATCH 116/404] fix: we are getting there...this should be it (#14158) --- .github/workflows/pkg.pr.new-comment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pkg.pr.new-comment.yml b/.github/workflows/pkg.pr.new-comment.yml index 65038c9a4c..c3924799d8 100644 --- a/.github/workflows/pkg.pr.new-comment.yml +++ b/.github/workflows/pkg.pr.new-comment.yml @@ -79,7 +79,7 @@ jobs: console.log('Publish Information'); console.log('='.repeat(50)); console.log('\nPublished Packages:'); - console.log(packages); + console.log(output.packages.map((p) => `${p.name} - pnpm add https://pkg.pr.new/${p.name}@${number}`).join('\n')); console.log('\nPlayground URL:'); console.log(`\nhttps://svelte.dev/playground?version=commit-${output.sha.substring(0, 7)}`) console.log('\n' + '='.repeat(50)); From e786729555c58ce8ab4409e6c0de1780d65d205d Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Tue, 5 Nov 2024 17:05:21 +0100 Subject: [PATCH 117/404] fix: find the right sha for logging (#14159) --- .github/workflows/pkg.pr.new-comment.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pkg.pr.new-comment.yml b/.github/workflows/pkg.pr.new-comment.yml index c3924799d8..1698a456d3 100644 --- a/.github/workflows/pkg.pr.new-comment.yml +++ b/.github/workflows/pkg.pr.new-comment.yml @@ -75,13 +75,17 @@ jobs: } async function log_publish_info() { + const svelte_package = output.packages.find(p => p.name === 'svelte'); + const svelte_sha = svelte_package.url.replace(/^.+@([^@]+)$/, '$1'); console.log('\n' + '='.repeat(50)); console.log('Publish Information'); console.log('='.repeat(50)); console.log('\nPublished Packages:'); - console.log(output.packages.map((p) => `${p.name} - pnpm add https://pkg.pr.new/${p.name}@${number}`).join('\n')); - console.log('\nPlayground URL:'); - console.log(`\nhttps://svelte.dev/playground?version=commit-${output.sha.substring(0, 7)}`) + console.log(output.packages.map((p) => `${p.name} - pnpm add https://pkg.pr.new/${p.name}@${p.url.replace(/^.+@([^@]+)$/, '$1')}`).join('\n')); + if(svelte_sha){ + console.log('\nPlayground URL:'); + console.log(`\nhttps://svelte.dev/playground?version=commit-${svelte_sha}`) + } console.log('\n' + '='.repeat(50)); } From 927319c139a2bf729499ba4f576ced369a6ba41f Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 5 Nov 2024 13:51:50 -0500 Subject: [PATCH 118/404] fix: update links in JSDoc (#14165) --- .changeset/rotten-toys-fix.md | 5 +++++ packages/svelte/src/transition/index.js | 2 +- packages/svelte/types/index.d.ts | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/rotten-toys-fix.md diff --git a/.changeset/rotten-toys-fix.md b/.changeset/rotten-toys-fix.md new file mode 100644 index 0000000000..fdaff54c1b --- /dev/null +++ b/.changeset/rotten-toys-fix.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: update links in JSDoc diff --git a/packages/svelte/src/transition/index.js b/packages/svelte/src/transition/index.js index 3978d088a3..8cc054cba2 100644 --- a/packages/svelte/src/transition/index.js +++ b/packages/svelte/src/transition/index.js @@ -209,7 +209,7 @@ function assign(tar, src) { } /** - * The `crossfade` function creates a pair of [transitions](https://svelte.dev/docs#template-syntax-element-directives-transition-fn) called `send` and `receive`. When an element is 'sent', it looks for a corresponding element being 'received', and generates a transition that transforms the element to its counterpart's position and fades it out. When an element is 'received', the reverse happens. If there is no counterpart, the `fallback` transition is used. + * The `crossfade` function creates a pair of [transitions](https://svelte.dev/docs/svelte/transition) called `send` and `receive`. When an element is 'sent', it looks for a corresponding element being 'received', and generates a transition that transforms the element to its counterpart's position and fades it out. When an element is 'received', the reverse happens. If there is no counterpart, the `fallback` transition is used. * * @param {CrossfadeParams & { * fallback?: (node: Element, params: CrossfadeParams, intro: boolean) => TransitionConfig; diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index ca67923ee1..00ba2556d9 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -1947,7 +1947,7 @@ declare module 'svelte/transition' { getTotalLength(): number; }, { delay, speed, duration, easing }?: DrawParams | undefined): TransitionConfig; /** - * The `crossfade` function creates a pair of [transitions](https://svelte.dev/docs#template-syntax-element-directives-transition-fn) called `send` and `receive`. When an element is 'sent', it looks for a corresponding element being 'received', and generates a transition that transforms the element to its counterpart's position and fades it out. When an element is 'received', the reverse happens. If there is no counterpart, the `fallback` transition is used. + * The `crossfade` function creates a pair of [transitions](https://svelte.dev/docs/svelte/transition) called `send` and `receive`. When an element is 'sent', it looks for a corresponding element being 'received', and generates a transition that transforms the element to its counterpart's position and fades it out. When an element is 'received', the reverse happens. If there is no counterpart, the `fallback` transition is used. * * */ export function crossfade({ fallback, ...defaults }: CrossfadeParams & { From ecc9b97a5be9a475f6dcc6fc84b46d9ad9ebceba Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 13:54:18 -0500 Subject: [PATCH 119/404] Version Packages (#14097) Co-authored-by: github-actions[bot] --- .changeset/dirty-parrots-smell.md | 5 ----- .changeset/empty-hairs-love.md | 5 ----- .changeset/mean-seas-give.md | 5 ----- .changeset/modern-pets-punch.md | 5 ----- .changeset/polite-timers-tell.md | 5 ----- .changeset/purple-owls-hug.md | 5 ----- .changeset/rotten-toys-fix.md | 5 ----- .changeset/selfish-chicken-argue.md | 5 ----- .changeset/short-lions-search.md | 5 ----- .changeset/silent-tigers-lay.md | 5 ----- .changeset/silly-bulldogs-rest.md | 5 ----- .changeset/warm-eyes-protect.md | 5 ----- .changeset/yellow-pumas-rule.md | 5 ----- packages/svelte/CHANGELOG.md | 30 +++++++++++++++++++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 16 files changed, 32 insertions(+), 67 deletions(-) delete mode 100644 .changeset/dirty-parrots-smell.md delete mode 100644 .changeset/empty-hairs-love.md delete mode 100644 .changeset/mean-seas-give.md delete mode 100644 .changeset/modern-pets-punch.md delete mode 100644 .changeset/polite-timers-tell.md delete mode 100644 .changeset/purple-owls-hug.md delete mode 100644 .changeset/rotten-toys-fix.md delete mode 100644 .changeset/selfish-chicken-argue.md delete mode 100644 .changeset/short-lions-search.md delete mode 100644 .changeset/silent-tigers-lay.md delete mode 100644 .changeset/silly-bulldogs-rest.md delete mode 100644 .changeset/warm-eyes-protect.md delete mode 100644 .changeset/yellow-pumas-rule.md diff --git a/.changeset/dirty-parrots-smell.md b/.changeset/dirty-parrots-smell.md deleted file mode 100644 index 50864d20a4..0000000000 --- a/.changeset/dirty-parrots-smell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure non-matching elements are scoped for `:not(...)` selector diff --git a/.changeset/empty-hairs-love.md b/.changeset/empty-hairs-love.md deleted file mode 100644 index a56e6c6334..0000000000 --- a/.changeset/empty-hairs-love.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure video elements autoplay in safari diff --git a/.changeset/mean-seas-give.md b/.changeset/mean-seas-give.md deleted file mode 100644 index 0c16ac6d55..0000000000 --- a/.changeset/mean-seas-give.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure trailing multiline comments on props produce correct code (#14143#issuecomment-2455702689) diff --git a/.changeset/modern-pets-punch.md b/.changeset/modern-pets-punch.md deleted file mode 100644 index 981547e9e5..0000000000 --- a/.changeset/modern-pets-punch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: correctly infer `` tag namespace diff --git a/.changeset/polite-timers-tell.md b/.changeset/polite-timers-tell.md deleted file mode 100644 index b98a89c95f..0000000000 --- a/.changeset/polite-timers-tell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: check options namespace for top level `svelte:element`s diff --git a/.changeset/purple-owls-hug.md b/.changeset/purple-owls-hug.md deleted file mode 100644 index f3dcb0a5e3..0000000000 --- a/.changeset/purple-owls-hug.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure migrate keeps inline/trailing comments in $props type definition diff --git a/.changeset/rotten-toys-fix.md b/.changeset/rotten-toys-fix.md deleted file mode 100644 index fdaff54c1b..0000000000 --- a/.changeset/rotten-toys-fix.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: update links in JSDoc diff --git a/.changeset/selfish-chicken-argue.md b/.changeset/selfish-chicken-argue.md deleted file mode 100644 index 32e0d0afef..0000000000 --- a/.changeset/selfish-chicken-argue.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure SvelteMap and SvelteSet work with generators in dev diff --git a/.changeset/short-lions-search.md b/.changeset/short-lions-search.md deleted file mode 100644 index ed3abdc053..0000000000 --- a/.changeset/short-lions-search.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: only output the key for each_key_duplicate diff --git a/.changeset/silent-tigers-lay.md b/.changeset/silent-tigers-lay.md deleted file mode 100644 index 8debfd7151..0000000000 --- a/.changeset/silent-tigers-lay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: prevent migrated snippet from shadow snippet prop diff --git a/.changeset/silly-bulldogs-rest.md b/.changeset/silly-bulldogs-rest.md deleted file mode 100644 index f92eb81575..0000000000 --- a/.changeset/silly-bulldogs-rest.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: pass along `anchor` in legacy class wrappers diff --git a/.changeset/warm-eyes-protect.md b/.changeset/warm-eyes-protect.md deleted file mode 100644 index fbb971f314..0000000000 --- a/.changeset/warm-eyes-protect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: recognize all custom element prop definitions diff --git a/.changeset/yellow-pumas-rule.md b/.changeset/yellow-pumas-rule.md deleted file mode 100644 index 02def3c178..0000000000 --- a/.changeset/yellow-pumas-rule.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: migrate multiple declarations with only some exported correctly diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 0786d007f3..cdda3a12c3 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,35 @@ # svelte +## 5.1.10 + +### Patch Changes + +- fix: ensure non-matching elements are scoped for `:not(...)` selector ([#13999](https://github.com/sveltejs/svelte/pull/13999)) + +- fix: ensure video elements autoplay in safari ([#14095](https://github.com/sveltejs/svelte/pull/14095)) + +- fix: ensure trailing multiline comments on props produce correct code (#14143#issuecomment-2455702689) ([#14143](https://github.com/sveltejs/svelte/pull/14143)) + +- fix: correctly infer `` tag namespace ([#14134](https://github.com/sveltejs/svelte/pull/14134)) + +- fix: check options namespace for top level `svelte:element`s ([#14101](https://github.com/sveltejs/svelte/pull/14101)) + +- fix: ensure migrate keeps inline/trailing comments in $props type definition ([#14143](https://github.com/sveltejs/svelte/pull/14143)) + +- fix: update links in JSDoc ([#14165](https://github.com/sveltejs/svelte/pull/14165)) + +- fix: ensure SvelteMap and SvelteSet work with generators in dev ([#14103](https://github.com/sveltejs/svelte/pull/14103)) + +- fix: only output the key for each_key_duplicate ([#14147](https://github.com/sveltejs/svelte/pull/14147)) + +- fix: prevent migrated snippet from shadow snippet prop ([#14127](https://github.com/sveltejs/svelte/pull/14127)) + +- fix: pass along `anchor` in legacy class wrappers ([#14100](https://github.com/sveltejs/svelte/pull/14100)) + +- fix: recognize all custom element prop definitions ([#14084](https://github.com/sveltejs/svelte/pull/14084)) + +- fix: migrate multiple declarations with only some exported correctly ([#14126](https://github.com/sveltejs/svelte/pull/14126)) + ## 5.1.9 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 257d3924bf..5234e334b6 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.1.9", + "version": "5.1.10", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index e6a9988e3a..d91ee8164e 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.1.9'; +export const VERSION = '5.1.10'; export const PUBLIC_VERSION = '5'; From 5077061695a00f8e6fccc37cf7fd66490e061ae4 Mon Sep 17 00:00:00 2001 From: Caique Torres <56696506+caiquetorres@users.noreply.github.com> Date: Tue, 5 Nov 2024 16:29:38 -0300 Subject: [PATCH 120/404] fix: issue with implicit public modifier causing undefined properties (#14153) closes #14152 --- .changeset/four-countries-taste.md | 5 +++++ .../phases/1-parse/remove_typescript_nodes.js | 2 +- .../runtime-runes/samples/typescript/main.svelte | 5 ++++- .../ts-unsupported-modifier-readonly/errors.json | 14 ++++++++++++++ .../ts-unsupported-modifier-readonly/input.svelte | 5 +++++ 5 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 .changeset/four-countries-taste.md create mode 100644 packages/svelte/tests/validator/samples/ts-unsupported-modifier-readonly/errors.json create mode 100644 packages/svelte/tests/validator/samples/ts-unsupported-modifier-readonly/input.svelte diff --git a/.changeset/four-countries-taste.md b/.changeset/four-countries-taste.md new file mode 100644 index 0000000000..6873b2ae2d --- /dev/null +++ b/.changeset/four-countries-taste.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: error on TypeScript's `readonly` modifier 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 edbb673bd5..5febef420d 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,7 +94,7 @@ const visitors = { e.typescript_invalid_feature(node, 'enums'); }, TSParameterProperty(node, context) { - if (node.accessibility && context.path.at(-2)?.kind === 'constructor') { + if ((node.readonly || node.accessibility) && context.path.at(-2)?.kind === 'constructor') { e.typescript_invalid_feature(node, 'accessibility modifiers on constructor parameters'); } return node.parameter; diff --git a/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte b/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte index d1c4f361ba..6c9cacdeff 100644 --- a/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte @@ -9,7 +9,10 @@ } class Foo { - constructor(readonly name: string) {} + public name: string; + constructor(name: string) { + this.name = name; + } } declare const declared_const: number; diff --git a/packages/svelte/tests/validator/samples/ts-unsupported-modifier-readonly/errors.json b/packages/svelte/tests/validator/samples/ts-unsupported-modifier-readonly/errors.json new file mode 100644 index 0000000000..44399179c0 --- /dev/null +++ b/packages/svelte/tests/validator/samples/ts-unsupported-modifier-readonly/errors.json @@ -0,0 +1,14 @@ +[ + { + "code": "typescript_invalid_feature", + "message": "TypeScript language features like accessibility modifiers on constructor parameters are not natively supported, and their use is generally discouraged. Outside of ` From aa23415179364d7f50f652c4e1a704e5a5d2ff16 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 6 Nov 2024 13:49:37 +0100 Subject: [PATCH 123/404] fix: remove scoping for most `:not` selectors (#14177) fixes #14168 This reverts the whole "selectors inside `:not` are scoped" logic. Scoping is done so that styles don't bleed. But within `:not`,everything is reversed, which means scoping the selectors now means they are more likely to bleed. That is the opposite of what we want to achieve, therefore we should just leave those selectors alone. The exception are `:not` selectors with descendant selectors, as that means "look up the tree" and we need to scope all ancestor elements in that case. --- .changeset/quick-eels-occur.md | 5 ++ packages/svelte/src/compiler/migrate/index.js | 6 +- .../phases/2-analyze/css/css-prune.js | 80 +++++++++---------- .../compiler/phases/3-transform/css/index.js | 43 +++++----- .../samples/not-selector-global/_config.js | 17 +--- .../samples/not-selector-global/expected.css | 26 ++++-- .../samples/not-selector-global/expected.html | 4 +- .../samples/not-selector-global/input.svelte | 20 ++++- .../_config.js | 5 -- .../expected.css | 4 - .../expected.html | 1 - .../input.svelte | 8 -- .../tests/css/samples/not-selector/_config.js | 38 +++------ .../css/samples/not-selector/expected.css | 25 +++--- .../css/samples/not-selector/expected.html | 4 +- .../css/samples/not-selector/input.svelte | 19 ++++- packages/svelte/tests/css/test.ts | 28 +++---- .../samples/is-not-where-has/output.svelte | 10 +-- 18 files changed, 172 insertions(+), 171 deletions(-) create mode 100644 .changeset/quick-eels-occur.md delete mode 100644 packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/_config.js delete mode 100644 packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/expected.css delete mode 100644 packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/expected.html delete mode 100644 packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/input.svelte diff --git a/.changeset/quick-eels-occur.md b/.changeset/quick-eels-occur.md new file mode 100644 index 0000000000..e5eb153fbf --- /dev/null +++ b/.changeset/quick-eels-occur.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: remove scoping for `:not` selectors diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index 8e8670c824..387b6a5485 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -50,9 +50,9 @@ function migrate_css(state) { while (code) { if ( code.startsWith(':has') || - code.startsWith(':not') || code.startsWith(':is') || - code.startsWith(':where') + code.startsWith(':where') || + code.startsWith(':not') ) { let start = code.indexOf('(') + 1; let is_global = false; @@ -74,7 +74,7 @@ function migrate_css(state) { char = code[end]; } if (start && end) { - if (!is_global) { + if (!is_global && !code.startsWith(':not')) { str.prependLeft(starting + start, ':global('); str.appendRight(starting + end - 1, ')'); } 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 fae89f11ed..4306d49d81 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 @@ -10,7 +10,6 @@ import { get_attribute_chunks, is_text_attribute } from '../../../utils/ast.js'; * stylesheet: Compiler.Css.StyleSheet; * element: Compiler.AST.RegularElement | Compiler.AST.SvelteElement; * from_render_tag: boolean; - * inside_not: boolean; * }} State */ /** @typedef {NODE_PROBABLY_EXISTS | NODE_DEFINITELY_EXISTS} NodeExistsValue */ @@ -62,13 +61,9 @@ export function prune(stylesheet, element) { const parent = get_element_parent(element); if (!parent) return; - walk( - stylesheet, - { stylesheet, element: parent, from_render_tag: true, inside_not: false }, - visitors - ); + walk(stylesheet, { stylesheet, element: parent, from_render_tag: true }, visitors); } else { - walk(stylesheet, { stylesheet, element, from_render_tag: false, inside_not: false }, visitors); + walk(stylesheet, { stylesheet, element, from_render_tag: false }, visitors); } } @@ -179,9 +174,9 @@ function truncate(node) { selector.type === 'PseudoClassSelector' && selector.args !== null && (selector.name === 'has' || - selector.name === 'not' || selector.name === 'is' || - selector.name === 'where') + selector.name === 'where' || + selector.name === 'not') )) ); }); @@ -227,7 +222,7 @@ function apply_selector(relative_selectors, rule, element, state) { // if this is the left-most non-global selector, mark it — we want // `x y z {...}` to become `x.blah y z.blah {...}` const parent = parent_selectors[parent_selectors.length - 1]; - if (!state.inside_not && (!parent || is_global(parent, rule))) { + if (!parent || is_global(parent, rule)) { mark(relative_selector, element); } @@ -269,7 +264,7 @@ function apply_combinator(combinator, relative_selector, parent_selectors, rule, if (parent.type === 'RegularElement' || parent.type === 'SvelteElement') { if (apply_selector(parent_selectors, rule, parent, state)) { // TODO the `name === ' '` causes false positives, but removing it causes false negatives... - if (!state.inside_not && (name === ' ' || crossed_component_boundary)) { + if (name === ' ' || crossed_component_boundary) { mark(parent_selectors[parent_selectors.length - 1], parent); } @@ -295,15 +290,11 @@ function apply_combinator(combinator, relative_selector, parent_selectors, rule, if (possible_sibling.type === 'RenderTag' || possible_sibling.type === 'SlotElement') { // `{@render foo()}

foo

` with `:global(.x) + p` is a match if (parent_selectors.length === 1 && parent_selectors[0].metadata.is_global) { - if (!state.inside_not) { - mark(relative_selector, element); - } + mark(relative_selector, element); sibling_matched = true; } } else if (apply_selector(parent_selectors, rule, possible_sibling, state)) { - if (!state.inside_not) { - mark(relative_selector, element); - } + mark(relative_selector, element); sibling_matched = true; } } @@ -512,7 +503,35 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element, // We came across a :global, everything beyond it is global and therefore a potential match if (name === 'global' && selector.args === null) return true; - if ((name === 'is' || name === 'where' || name === 'not') && selector.args) { + // :not(...) contents should stay unscoped. Scoping them would achieve the opposite of what we want, + // because they are then _more_ likely to bleed out of the component. The exception is complex selectors + // with descendants, in which case we scope them all. + if (name === 'not' && selector.args) { + for (const complex_selector of selector.args.children) { + complex_selector.metadata.used = true; + const relative = truncate(complex_selector); + + if (complex_selector.children.length > 1) { + // foo:not(bar foo) means that bar is an ancestor of foo (side note: ending with foo is the only way the selector make sense). + // We can't fully check if that actually matches with our current algorithm, so we just assume it does. + // The result may not match a real element, so the only drawback is the missing prune. + for (const selector of relative) { + selector.metadata.scoped = true; + } + + /** @type {Compiler.AST.RegularElement | Compiler.AST.SvelteElement | null} */ + let el = element; + while (el) { + el.metadata.scoped = true; + el = get_element_parent(el); + } + } + } + + break; + } + + if ((name === 'is' || name === 'where') && selector.args) { let matched = false; for (const complex_selector of selector.args.children) { @@ -522,32 +541,9 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element, if (is_global) { complex_selector.metadata.used = true; matched = true; - } else if (name !== 'not' && apply_selector(relative, rule, element, state)) { + } else if (apply_selector(relative, rule, element, state)) { complex_selector.metadata.used = true; matched = true; - } else if ( - name === 'not' && - !apply_selector(relative, rule, element, { ...state, inside_not: true }) - ) { - // For `:not(...)` we gotta do the inverse: If it did not match, mark the element and possibly - // everything above (if the selector is written is a such) as scoped (because they matched by not matching). - element.metadata.scoped = true; - complex_selector.metadata.used = true; - matched = true; - - for (const r of relative) { - r.metadata.scoped = true; - } - - // bar:not(foo bar) means that foo is an ancestor of bar - if (complex_selector.children.length > 1) { - /** @type {Compiler.AST.RegularElement | Compiler.AST.SvelteElement | null} */ - let el = get_element_parent(element); - while (el) { - el.metadata.scoped = true; - el = get_element_parent(el); - } - } } else if (complex_selector.children.length > 1 && (name == 'is' || name == 'where')) { // foo :is(bar baz) can also mean that bar is an ancestor of foo, and baz a descendant. // We can't fully check if that actually matches with our current algorithm, so we just assume it does. 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 62c520e994..350eca35d8 100644 --- a/packages/svelte/src/compiler/phases/3-transform/css/index.js +++ b/packages/svelte/src/compiler/phases/3-transform/css/index.js @@ -278,29 +278,10 @@ const visitors = { ComplexSelector(node, context) { const before_bumped = context.state.specificity.bumped; - /** - * @param {Css.PseudoClassSelector} selector - * @param {Css.Combinator | null} combinator - */ - function remove_global_pseudo_class(selector, combinator) { - if (selector.args === null) { - let start = selector.start; - if (combinator?.name === ' ') { - // div :global.x becomes div.x - while (/\s/.test(context.state.code.original[start - 1])) start--; - } - context.state.code.remove(start, selector.start + ':global'.length); - } else { - context.state.code - .remove(selector.start, selector.start + ':global('.length) - .remove(selector.end - 1, selector.end); - } - } - for (const relative_selector of node.children) { if (relative_selector.metadata.is_global) { const global = /** @type {Css.PseudoClassSelector} */ (relative_selector.selectors[0]); - remove_global_pseudo_class(global, relative_selector.combinator); + remove_global_pseudo_class(global, relative_selector.combinator, context.state); if ( node.metadata.rule?.metadata.parent_rule && @@ -328,7 +309,7 @@ const visitors = { // for any :global() or :global at the middle of compound selector for (const selector of relative_selector.selectors) { if (selector.type === 'PseudoClassSelector' && selector.name === 'global') { - remove_global_pseudo_class(selector, null); + remove_global_pseudo_class(selector, null, context.state); } } @@ -380,6 +361,26 @@ const visitors = { } }; +/** + * @param {Css.PseudoClassSelector} selector + * @param {Css.Combinator | null} combinator + * @param {State} state + */ +function remove_global_pseudo_class(selector, combinator, state) { + if (selector.args === null) { + let start = selector.start; + if (combinator?.name === ' ') { + // div :global.x becomes div.x + while (/\s/.test(state.code.original[start - 1])) start--; + } + state.code.remove(start, selector.start + ':global'.length); + } else { + state.code + .remove(selector.start, selector.start + ':global('.length) + .remove(selector.end - 1, selector.end); + } +} + /** * Walk backwards until we find a non-whitespace character * @param {number} end diff --git a/packages/svelte/tests/css/samples/not-selector-global/_config.js b/packages/svelte/tests/css/samples/not-selector-global/_config.js index 2776ac227a..292c6c49ac 100644 --- a/packages/svelte/tests/css/samples/not-selector-global/_config.js +++ b/packages/svelte/tests/css/samples/not-selector-global/_config.js @@ -1,20 +1,5 @@ import { test } from '../../test'; export default test({ - warnings: [ - { - code: 'css_unused_selector', - message: 'Unused CSS selector ":global(.x) :not(p)"', - start: { - line: 14, - column: 1, - character: 197 - }, - end: { - line: 14, - column: 20, - character: 216 - } - } - ] + warnings: [] }); diff --git a/packages/svelte/tests/css/samples/not-selector-global/expected.css b/packages/svelte/tests/css/samples/not-selector-global/expected.css index f90edd0c88..ea9ff39486 100644 --- a/packages/svelte/tests/css/samples/not-selector-global/expected.css +++ b/packages/svelte/tests/css/samples/not-selector-global/expected.css @@ -2,18 +2,28 @@ .svelte-xyz:not(.foo) { color: green; } - .svelte-xyz:not(.foo:where(.svelte-xyz)):not(.unused) { + .svelte-xyz:not(.foo):not(.unused) { color: green; } - .x:not(.foo.svelte-xyz) { + .x:not(.foo) { color: green; } - /* (unused) :global(.x) :not(p) { - color: red; - }*/ - .x:not(p.svelte-xyz) { - color: red; /* TODO would be nice to prune this one day */ + .x .svelte-xyz:not(p) { + color: green; + } + .x:not(p) { + color: green; + } + .x .svelte-xyz:not(.unused) { + color: green; + } + + span:not(p.svelte-xyz span:where(.svelte-xyz)) { + color: green; + } + span.svelte-xyz:not(p span) { + color: green; } - .x .svelte-xyz:not(.unused:where(.svelte-xyz)) { + span:not(p span) { color: green; } diff --git a/packages/svelte/tests/css/samples/not-selector-global/expected.html b/packages/svelte/tests/css/samples/not-selector-global/expected.html index b4834bb818..5eeb2be294 100644 --- a/packages/svelte/tests/css/samples/not-selector-global/expected.html +++ b/packages/svelte/tests/css/samples/not-selector-global/expected.html @@ -1 +1,3 @@ -

foo

bar

\ No newline at end of file +

foo

+

bar baz

+buzz \ No newline at end of file diff --git a/packages/svelte/tests/css/samples/not-selector-global/input.svelte b/packages/svelte/tests/css/samples/not-selector-global/input.svelte index 578052d943..1f0e6cd6db 100644 --- a/packages/svelte/tests/css/samples/not-selector-global/input.svelte +++ b/packages/svelte/tests/css/samples/not-selector-global/input.svelte @@ -1,5 +1,9 @@

foo

-

bar

+

+ bar + baz +

+buzz \ No newline at end of file diff --git a/packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/_config.js b/packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/_config.js deleted file mode 100644 index 292c6c49ac..0000000000 --- a/packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/_config.js +++ /dev/null @@ -1,5 +0,0 @@ -import { test } from '../../test'; - -export default test({ - warnings: [] -}); diff --git a/packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/expected.css b/packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/expected.css deleted file mode 100644 index 8fe09de6dc..0000000000 --- a/packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/expected.css +++ /dev/null @@ -1,4 +0,0 @@ - - .svelte-xyz:not(.bar:where(.svelte-xyz)) { - color: green; - } diff --git a/packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/expected.html b/packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/expected.html deleted file mode 100644 index fa4ca95c60..0000000000 --- a/packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/expected.html +++ /dev/null @@ -1 +0,0 @@ -

foo

bar

\ No newline at end of file diff --git a/packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/input.svelte b/packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/input.svelte deleted file mode 100644 index 6599a21ca6..0000000000 --- a/packages/svelte/tests/css/samples/not-selector-hash-on-right-elements/input.svelte +++ /dev/null @@ -1,8 +0,0 @@ -

foo

-

bar

- - \ No newline at end of file diff --git a/packages/svelte/tests/css/samples/not-selector/_config.js b/packages/svelte/tests/css/samples/not-selector/_config.js index 27b4f91a6a..9c91a2a4c0 100644 --- a/packages/svelte/tests/css/samples/not-selector/_config.js +++ b/packages/svelte/tests/css/samples/not-selector/_config.js @@ -4,44 +4,30 @@ export default test({ warnings: [ { code: 'css_unused_selector', - message: 'Unused CSS selector ":not(p)"', + message: 'Unused CSS selector "span :not(.foo)"', start: { - line: 11, + line: 26, column: 1, - character: 125 + character: 276 }, end: { - line: 11, - column: 8, - character: 132 - } - }, - { - code: 'css_unused_selector', - message: 'Unused CSS selector "p :not(.foo)"', - start: { - line: 22, - column: 1, - character: 235 - }, - end: { - line: 22, - column: 13, - character: 247 + line: 26, + column: 16, + character: 291 } }, { code: 'css_unused_selector', - message: 'Unused CSS selector "p :not(.unused)"', + message: 'Unused CSS selector "span :not(.unused)"', start: { - line: 25, + line: 29, column: 1, - character: 268 + character: 312 }, end: { - line: 25, - column: 16, - character: 283 + line: 29, + column: 19, + character: 330 } } ] diff --git a/packages/svelte/tests/css/samples/not-selector/expected.css b/packages/svelte/tests/css/samples/not-selector/expected.css index b4d51bf269..ea0b3aacac 100644 --- a/packages/svelte/tests/css/samples/not-selector/expected.css +++ b/packages/svelte/tests/css/samples/not-selector/expected.css @@ -1,24 +1,31 @@ - .svelte-xyz:not(.foo:where(.svelte-xyz)) { + .svelte-xyz:not(.foo) { color: green; } - .svelte-xyz:not(.unused:where(.svelte-xyz)) { + .svelte-xyz:not(.unused) { + color: green; + } + .svelte-xyz:not(p) { color: green; } - /* (unused) :not(p) { - color: red; - }*/ - .svelte-xyz:not(.foo:where(.svelte-xyz)):not(.unused:where(.svelte-xyz)) { + .svelte-xyz:not(.foo):not(.unused) { color: green; } - p.svelte-xyz:not(.foo:where(.svelte-xyz)) { + p.svelte-xyz:not(.foo) { color: green; } - /* (unused) p :not(.foo) { + /* (unused) span :not(.foo) { color: red; }*/ - /* (unused) p :not(.unused) { + /* (unused) span :not(.unused) { color: red; }*/ + + span.svelte-xyz:not(p:where(.svelte-xyz) span:where(.svelte-xyz)) { + color: green; + } + span.svelte-xyz:not(:focus) { + color: green; + } diff --git a/packages/svelte/tests/css/samples/not-selector/expected.html b/packages/svelte/tests/css/samples/not-selector/expected.html index b4834bb818..5eeb2be294 100644 --- a/packages/svelte/tests/css/samples/not-selector/expected.html +++ b/packages/svelte/tests/css/samples/not-selector/expected.html @@ -1 +1,3 @@ -

foo

bar

\ No newline at end of file +

foo

+

bar baz

+buzz \ No newline at end of file diff --git a/packages/svelte/tests/css/samples/not-selector/input.svelte b/packages/svelte/tests/css/samples/not-selector/input.svelte index a0e72c7be8..3a786530a2 100644 --- a/packages/svelte/tests/css/samples/not-selector/input.svelte +++ b/packages/svelte/tests/css/samples/not-selector/input.svelte @@ -1,5 +1,9 @@

foo

-

bar

+

+ bar + baz +

+buzz \ No newline at end of file diff --git a/packages/svelte/tests/css/test.ts b/packages/svelte/tests/css/test.ts index 30ad96369a..443ee02958 100644 --- a/packages/svelte/tests/css/test.ts +++ b/packages/svelte/tests/css/test.ts @@ -32,30 +32,17 @@ const { test, run } = suite(async (config, cwd) => { await compile_directory(cwd, 'client', { cssHash: () => 'svelte-xyz', ...config.compileOptions }); await compile_directory(cwd, 'server', { cssHash: () => 'svelte-xyz', ...config.compileOptions }); - 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(); - - assert.equal(dom_css, ssr_css); - - const dom_warnings = load_warnings(`${cwd}/_output/client/input.svelte.warnings.json`); - const ssr_warnings = load_warnings(`${cwd}/_output/server/input.svelte.warnings.json`); - const expected_warnings = (config.warnings || []).map(normalize_warning); - assert.deepEqual(dom_warnings, ssr_warnings); - assert.deepEqual(dom_warnings.map(normalize_warning), expected_warnings); - const expected = { html: try_read_file(`${cwd}/expected.html`), css: try_read_file(`${cwd}/expected.css`) }; - assert.equal(dom_css.trim().replace(/\r\n/g, '\n'), (expected.css ?? '').trim()); - // we do this here, rather than in the expected.html !== null // block, to verify that valid code was generated const ClientComponent = (await import(`${cwd}/_output/client/input.svelte.js`)).default; const ServerComponent = (await import(`${cwd}/_output/server/input.svelte.js`)).default; - // verify that the right elements have scoping selectors + // verify that the right elements have scoping selectors (do this first to ensure all actual files are written to disk) if (expected.html !== null) { const target = window.document.createElement('main'); @@ -75,6 +62,19 @@ const { test, run } = suite(async (config, cwd) => { // const actual_ssr = ServerComponent.render(config.props).html; // assert_html_equal(actual_ssr, expected.html); } + + 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(); + + assert.equal(dom_css, ssr_css); + + const dom_warnings = load_warnings(`${cwd}/_output/client/input.svelte.warnings.json`); + const ssr_warnings = load_warnings(`${cwd}/_output/server/input.svelte.warnings.json`); + const expected_warnings = (config.warnings || []).map(normalize_warning); + assert.deepEqual(dom_warnings, ssr_warnings); + assert.deepEqual(dom_warnings.map(normalize_warning), expected_warnings); + + assert.equal(dom_css.trim().replace(/\r\n/g, '\n'), (expected.css ?? '').trim()); }); export { test }; diff --git a/packages/svelte/tests/migrate/samples/is-not-where-has/output.svelte b/packages/svelte/tests/migrate/samples/is-not-where-has/output.svelte index 1e02e9bdfd..d8bb703ac5 100644 --- a/packages/svelte/tests/migrate/samples/is-not-where-has/output.svelte +++ b/packages/svelte/tests/migrate/samples/is-not-where-has/output.svelte @@ -21,22 +21,22 @@ what if i'm talking about `:has()` in my blog? \ No newline at end of file diff --git a/packages/svelte/tests/css/samples/nesting-selectors/_config.js b/packages/svelte/tests/css/samples/nesting-selectors/_config.js new file mode 100644 index 0000000000..8960eff1b6 --- /dev/null +++ b/packages/svelte/tests/css/samples/nesting-selectors/_config.js @@ -0,0 +1,48 @@ +import { test } from '../../test'; + +export default test({ + warnings: [ + { + code: 'css_unused_selector', + message: 'Unused CSS selector ".unused:has(&)"', + start: { + line: 10, + column: 2, + character: 105 + }, + end: { + line: 10, + column: 16, + character: 119 + } + }, + { + code: 'css_unused_selector', + message: 'Unused CSS selector "&.unused"', + start: { + line: 23, + column: 3, + character: 223 + }, + end: { + line: 23, + column: 11, + character: 231 + } + }, + { + code: 'css_unused_selector', + message: 'Unused CSS selector "&.unused"', + start: { + line: 37, + column: 3, + character: 344 + }, + end: { + line: 37, + column: 11, + character: 352 + } + } + ] +}); diff --git a/packages/svelte/tests/css/samples/nesting-selectors/expected.css b/packages/svelte/tests/css/samples/nesting-selectors/expected.css new file mode 100644 index 0000000000..1b606b10ca --- /dev/null +++ b/packages/svelte/tests/css/samples/nesting-selectors/expected.css @@ -0,0 +1,37 @@ + + nav.svelte-xyz { + header:where(.svelte-xyz):has(&){ + color: green; + } + /* (unused) .unused:has(&){ + color: red; + }*/ + } + + header.svelte-xyz { + > nav:where(.svelte-xyz) { + color: green; + + &.active { + color: green; + } + + /* (unused) &.unused { + color: red; + }*/ + } + } + + header.svelte-xyz { + & > nav:where(.svelte-xyz) { + color: green; + + &.active { + color: green; + } + + /* (unused) &.unused { + color: red; + }*/ + } + } diff --git a/packages/svelte/tests/css/samples/nesting-selectors/expected.html b/packages/svelte/tests/css/samples/nesting-selectors/expected.html new file mode 100644 index 0000000000..59e17c2d02 --- /dev/null +++ b/packages/svelte/tests/css/samples/nesting-selectors/expected.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/packages/svelte/tests/css/samples/nesting-selectors/input.svelte b/packages/svelte/tests/css/samples/nesting-selectors/input.svelte new file mode 100644 index 0000000000..98881237c0 --- /dev/null +++ b/packages/svelte/tests/css/samples/nesting-selectors/input.svelte @@ -0,0 +1,42 @@ +
+ +
+ + \ No newline at end of file From ea0d80e1950b5c62547c69a26c823282e8d88ad3 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Thu, 7 Nov 2024 15:33:02 +0100 Subject: [PATCH 133/404] fix: consider variables with synthetic store sub as state (#14195) Fixes #14183 --- .changeset/giant-waves-act.md | 5 ++++ .../src/compiler/phases/2-analyze/index.js | 11 +++++++ .../variable-assigned-store-state/Test.svelte | 11 +++++++ .../variable-assigned-store-state/_config.js | 29 +++++++++++++++++++ .../variable-assigned-store-state/main.svelte | 9 ++++++ 5 files changed, 65 insertions(+) create mode 100644 .changeset/giant-waves-act.md create mode 100644 packages/svelte/tests/runtime-legacy/samples/variable-assigned-store-state/Test.svelte create mode 100644 packages/svelte/tests/runtime-legacy/samples/variable-assigned-store-state/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/variable-assigned-store-state/main.svelte diff --git a/.changeset/giant-waves-act.md b/.changeset/giant-waves-act.md new file mode 100644 index 0000000000..591f0f25ba --- /dev/null +++ b/.changeset/giant-waves-act.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: consider variables with synthetic store sub as state diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index f5a31d09a7..36d8145145 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -351,6 +351,17 @@ export function analyze_component(root, source, options) { } } + // if we are creating a synthetic binding for a let declaration we should also declare + // the declaration as state in case it's reassigned + if ( + declaration !== null && + declaration.kind === 'normal' && + declaration.declaration_kind === 'let' && + declaration.reassigned + ) { + declaration.kind = 'state'; + } + const binding = instance.scope.declare(b.id(name), 'store_sub', 'synthetic'); binding.references = references; instance.scope.references.set(name, references); diff --git a/packages/svelte/tests/runtime-legacy/samples/variable-assigned-store-state/Test.svelte b/packages/svelte/tests/runtime-legacy/samples/variable-assigned-store-state/Test.svelte new file mode 100644 index 0000000000..8b11acb279 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/variable-assigned-store-state/Test.svelte @@ -0,0 +1,11 @@ + + + +

{$currentStore}

\ No newline at end of file diff --git a/packages/svelte/tests/runtime-legacy/samples/variable-assigned-store-state/_config.js b/packages/svelte/tests/runtime-legacy/samples/variable-assigned-store-state/_config.js new file mode 100644 index 0000000000..eecdcb2462 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/variable-assigned-store-state/_config.js @@ -0,0 +1,29 @@ +import { flushSync } from 'svelte'; +import { ok, test } from '../../test'; + +export default test({ + async test({ assert, target, window }) { + const [btn1, btn2] = target.querySelectorAll('button'); + const p = target.querySelector('p'); + + assert.equal(p?.innerHTML, ''); + + flushSync(() => { + btn2.click(); + }); + + assert.equal(p?.innerHTML, '1'); + + flushSync(() => { + btn1.click(); + }); + + assert.equal(p?.innerHTML, '1'); + + flushSync(() => { + btn2.click(); + }); + + assert.equal(p?.innerHTML, '2'); + } +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/variable-assigned-store-state/main.svelte b/packages/svelte/tests/runtime-legacy/samples/variable-assigned-store-state/main.svelte new file mode 100644 index 0000000000..5033fbc526 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/variable-assigned-store-state/main.svelte @@ -0,0 +1,9 @@ + + + + \ No newline at end of file From 53af138d588f77bb8f4f10f9ad15fd4f798b50ef Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Thu, 7 Nov 2024 21:18:45 +0100 Subject: [PATCH 134/404] fix: read index as a source in legacy keyed each block (#14208) Fixes #14203 --- .changeset/late-dodos-film.md | 5 +++++ .../phases/3-transform/client/visitors/EachBlock.js | 4 ++-- .../keyed-each-bind-read-index/Component.svelte | 5 +++++ .../samples/keyed-each-bind-read-index/_config.js | 9 +++++++++ .../samples/keyed-each-bind-read-index/main.svelte | 10 ++++++++++ 5 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 .changeset/late-dodos-film.md create mode 100644 packages/svelte/tests/runtime-legacy/samples/keyed-each-bind-read-index/Component.svelte create mode 100644 packages/svelte/tests/runtime-legacy/samples/keyed-each-bind-read-index/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/keyed-each-bind-read-index/main.svelte diff --git a/.changeset/late-dodos-film.md b/.changeset/late-dodos-film.md new file mode 100644 index 0000000000..c3636019da --- /dev/null +++ b/.changeset/late-dodos-film.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: read index as a source in legacy keyed each block 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 fb6338cae3..55d7ded247 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 @@ -196,7 +196,7 @@ export function EachBlock(node, context) { // forbidden in runes mode return b.member( each_node_meta.array_name ? b.call(each_node_meta.array_name) : collection, - index, + (flags & EACH_INDEX_REACTIVE) !== 0 ? get_value(index) : index, true ); } @@ -208,7 +208,7 @@ export function EachBlock(node, context) { const left = b.member( each_node_meta.array_name ? b.call(each_node_meta.array_name) : collection, - index, + (flags & EACH_INDEX_REACTIVE) !== 0 ? get_value(index) : index, true ); diff --git a/packages/svelte/tests/runtime-legacy/samples/keyed-each-bind-read-index/Component.svelte b/packages/svelte/tests/runtime-legacy/samples/keyed-each-bind-read-index/Component.svelte new file mode 100644 index 0000000000..988c2b6457 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/keyed-each-bind-read-index/Component.svelte @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/packages/svelte/tests/runtime-legacy/samples/keyed-each-bind-read-index/_config.js b/packages/svelte/tests/runtime-legacy/samples/keyed-each-bind-read-index/_config.js new file mode 100644 index 0000000000..8b5db4df40 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/keyed-each-bind-read-index/_config.js @@ -0,0 +1,9 @@ +import { test } from '../../test'; + +export default test({ + async test({ assert, target, logs }) { + const p = target.querySelector('p'); + assert.equal(p?.innerHTML, '1'); + assert.deepEqual(logs, [0]); + } +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/keyed-each-bind-read-index/main.svelte b/packages/svelte/tests/runtime-legacy/samples/keyed-each-bind-read-index/main.svelte new file mode 100644 index 0000000000..384722f5aa --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/keyed-each-bind-read-index/main.svelte @@ -0,0 +1,10 @@ + + +{#each items as item, idx(item)} + +{/each} + +

{items}

\ No newline at end of file From 870745fc5370d0bb6b2c5186bffcc1602168a4a7 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Fri, 8 Nov 2024 10:53:43 +0100 Subject: [PATCH 135/404] fix: handle sibling combinators within `:has` (#14213) We didn't collect sibling elements of a given element to then check the `:has` selectors. This adds the logic for that. Fixes #14072 --- .changeset/four-jobs-care.md | 5 ++ .../phases/2-analyze/css/css-prune.js | 53 ++++++++++++++++++- .../svelte/tests/css/samples/has/_config.js | 14 +++++ .../svelte/tests/css/samples/has/expected.css | 10 ++++ .../svelte/tests/css/samples/has/input.svelte | 10 ++++ 5 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 .changeset/four-jobs-care.md diff --git a/.changeset/four-jobs-care.md b/.changeset/four-jobs-care.md new file mode 100644 index 0000000000..51e5d22313 --- /dev/null +++ b/.changeset/four-jobs-care.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: handle sibling combinators within `:has` 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 4207a16502..a9ea9fe913 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 @@ -412,6 +412,8 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element, const child_elements = []; /** @type {Array} */ const descendant_elements = []; + /** @type {Array} */ + let sibling_elements; // do them lazy because it's rarely used and expensive to calculate walk( /** @type {Compiler.SvelteNode} */ (element.fragment), @@ -457,7 +459,11 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element, } const descendants = - left_most_combinator.name === '>' ? child_elements : descendant_elements; + left_most_combinator.name === '+' || left_most_combinator.name === '~' + ? (sibling_elements ??= get_following_sibling_elements(element)) + : left_most_combinator.name === '>' + ? child_elements + : descendant_elements; let selector_matched = false; @@ -675,6 +681,51 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element, return true; } +/** @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement} element */ +function get_following_sibling_elements(element) { + /** @type {Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.Root | null} */ + let parent = get_element_parent(element); + + if (!parent) { + parent = element; + while (parent?.type !== 'Root') { + parent = /** @type {any} */ (parent).parent; + } + } + + /** @type {Array} */ + const sibling_elements = []; + let found_parent = false; + + for (const el of parent.fragment.nodes) { + if (found_parent) { + walk( + el, + {}, + { + RegularElement(node) { + sibling_elements.push(node); + }, + SvelteElement(node) { + sibling_elements.push(node); + } + } + ); + } else { + /** @type {any} */ + let child = element; + while (child !== el && child !== parent) { + child = child.parent; + } + if (child === el) { + found_parent = true; + } + } + } + + return sibling_elements; +} + /** * @param {any} operator * @param {any} expected_value diff --git a/packages/svelte/tests/css/samples/has/_config.js b/packages/svelte/tests/css/samples/has/_config.js index 675254c5b7..8b8e7760f5 100644 --- a/packages/svelte/tests/css/samples/has/_config.js +++ b/packages/svelte/tests/css/samples/has/_config.js @@ -127,6 +127,20 @@ export default test({ column: 11, character: 1134 } + }, + { + code: 'css_unused_selector', + message: 'Unused CSS selector "x:has(~ y)"', + start: { + line: 121, + column: 1, + character: 1326 + }, + end: { + line: 121, + column: 11, + character: 1336 + } } ] }); diff --git a/packages/svelte/tests/css/samples/has/expected.css b/packages/svelte/tests/css/samples/has/expected.css index 251a6952d5..4cb8d76bf6 100644 --- a/packages/svelte/tests/css/samples/has/expected.css +++ b/packages/svelte/tests/css/samples/has/expected.css @@ -101,3 +101,13 @@ x.svelte-xyz:has(y:where(.svelte-xyz)) + c:where(.svelte-xyz) { color: green; } + + x.svelte-xyz:has(+ c:where(.svelte-xyz)) { + color: green; + } + x.svelte-xyz:has(~ c:where(.svelte-xyz)) { + color: green; + } + /* (unused) x:has(~ y) { + color: red; + }*/ diff --git a/packages/svelte/tests/css/samples/has/input.svelte b/packages/svelte/tests/css/samples/has/input.svelte index 7a62b69c0b..8eb6296f05 100644 --- a/packages/svelte/tests/css/samples/has/input.svelte +++ b/packages/svelte/tests/css/samples/has/input.svelte @@ -111,4 +111,14 @@ x:has(y) + c { color: green; } + + x:has(+ c) { + color: green; + } + x:has(~ c) { + color: green; + } + x:has(~ y) { + color: red; + } From 438de04fb2a753287bfd9fd420711cc7cd971012 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Fri, 8 Nov 2024 11:18:50 +0100 Subject: [PATCH 136/404] fix: add migration task when there's a variable named that would conflict with a rune (#14216) Closes #14215 --- .changeset/chatty-gorillas-poke.md | 5 ++ packages/svelte/src/compiler/migrate/index.js | 50 +++++++++++++++++++ .../_config.js | 8 +++ .../input.svelte | 6 +++ .../output.svelte | 8 +++ .../_config.js | 8 +++ .../input.svelte | 9 ++++ .../output.svelte | 11 ++++ .../_config.js | 8 +++ .../input.svelte | 5 ++ .../output.svelte | 7 +++ .../_config.js | 8 +++ .../input.svelte | 6 +++ .../output.svelte | 7 +++ .../_config.js | 8 +++ .../input.svelte | 10 ++++ .../output.svelte | 12 +++++ .../_config.js | 8 +++ .../input.svelte | 4 ++ .../output.svelte | 6 +++ .../_config.js | 8 +++ .../input.svelte | 7 +++ .../output.svelte | 9 ++++ .../_config.js | 8 +++ .../input.svelte | 7 +++ .../output.svelte | 9 ++++ .../_config.js | 8 +++ .../input.svelte | 7 +++ .../output.svelte | 9 ++++ 29 files changed, 266 insertions(+) create mode 100644 .changeset/chatty-gorillas-poke.md create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$bindable-bindable-var-1/_config.js create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$bindable-bindable-var-1/input.svelte create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$bindable-bindable-var-1/output.svelte create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-1/_config.js create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-1/input.svelte create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-1/output.svelte create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-2/_config.js create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-2/input.svelte create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-2/output.svelte create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/_config.js create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/input.svelte create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/output.svelte create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-4/_config.js create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-4/input.svelte create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-4/output.svelte create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$props-props-var-1/_config.js create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$props-props-var-1/input.svelte create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$props-props-var-1/output.svelte create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-1/_config.js create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-1/input.svelte create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-1/output.svelte create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-2/_config.js create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-2/input.svelte create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-2/output.svelte create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-3/_config.js create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-3/input.svelte create mode 100644 packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-3/output.svelte diff --git a/.changeset/chatty-gorillas-poke.md b/.changeset/chatty-gorillas-poke.md new file mode 100644 index 0000000000..744adcf214 --- /dev/null +++ b/.changeset/chatty-gorillas-poke.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: add migration task when there's a variable named that would conflict with a rune diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index 387b6a5485..b7687af09f 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -239,6 +239,18 @@ export function migrate(source, { filename, use_ts } = {}) { insertion_point = state.props_insertion_point; + /** + * @param {"derived"|"props"|"bindable"} rune + */ + function check_rune_binding(rune) { + const has_rune_binding = !!state.scope.get(rune); + if (has_rune_binding) { + throw new MigrationError( + `migrating this component would require adding a \`$${rune}\` rune but there's already a variable named ${rune}.\n Rename the variable and try again or migrate by hand.` + ); + } + } + if (state.props.length > 0 || analysis.uses_rest_props || analysis.uses_props) { const has_many_props = state.props.length > 3; const newline_separator = `\n${indent}${indent}`; @@ -253,6 +265,7 @@ export function migrate(source, { filename, use_ts } = {}) { let prop_str = prop.local === prop.exported ? prop.local : `${prop.exported}: ${prop.local}`; if (prop.bindable) { + check_rune_binding('bindable'); prop_str += ` = $bindable(${prop.init})`; } else if (prop.init) { prop_str += ` = ${prop.init}`; @@ -300,11 +313,13 @@ export function migrate(source, { filename, use_ts } = {}) { if (type) { props_declaration = `${type}\n\n${indent}${props_declaration}`; } + check_rune_binding('props'); props_declaration = `${props_declaration}${type ? `: ${type_name}` : ''} = $props();`; } else { if (type) { props_declaration = `${state.props.length > 0 ? `${type}\n\n${indent}` : ''}/** @type {${state.props.length > 0 ? type_name : ''}${analysis.uses_props || analysis.uses_rest_props ? `${state.props.length > 0 ? ' & ' : ''}{ [key: string]: any }` : ''}} */\n${indent}${props_declaration}`; } + check_rune_binding('props'); props_declaration = `${props_declaration} = $props();`; } @@ -361,6 +376,7 @@ export function migrate(source, { filename, use_ts } = {}) { : insertion_point; if (state.derived_components.size > 0) { + check_rune_binding('derived'); str.appendRight( insertion_point, `\n${indent}${[...state.derived_components.entries()].map(([init, name]) => `const ${name} = $derived(${init});`).join(`\n${indent}`)}\n` @@ -368,6 +384,7 @@ export function migrate(source, { filename, use_ts } = {}) { } if (state.derived_conflicting_slots.size > 0) { + check_rune_binding('derived'); str.appendRight( insertion_point, `\n${indent}${[...state.derived_conflicting_slots.entries()].map(([name, init]) => `const ${name} = $derived(${init});`).join(`\n${indent}`)}\n` @@ -652,6 +669,18 @@ const instance_script = { continue; } + /** + * @param {"state"|"derived"} rune + */ + function check_rune_binding(rune) { + const has_rune_binding = !!state.scope.get(rune); + if (has_rune_binding) { + throw new MigrationError( + `can't migrate \`${state.str.original.substring(/** @type {number} */ (node.start), node.end)}\` to \`$${rune}\` because there's a variable named ${rune}.\n Rename the variable and try again or migrate by hand.` + ); + } + } + // state if (declarator.init) { let { start, end } = /** @type {{ start: number, end: number }} */ (declarator.init); @@ -661,6 +690,8 @@ const instance_script = { while (state.str.original[end - 1] !== ')') end += 1; } + check_rune_binding('state'); + state.str.prependLeft(start, '$state('); state.str.appendRight(end, ')'); } else { @@ -755,6 +786,8 @@ const instance_script = { } } + check_rune_binding('derived'); + // Someone wrote a `$: { ... }` statement which we can turn into a `$derived` state.str.appendRight( /** @type {number} */ (declarator.id.typeAnnotation?.end ?? declarator.id.end), @@ -795,6 +828,8 @@ const instance_script = { } } } else { + check_rune_binding('state'); + state.str.prependLeft( /** @type {number} */ (declarator.id.typeAnnotation?.end ?? declarator.id.end), ' = $state(' @@ -858,6 +893,18 @@ const instance_script = { next(); + /** + * @param {"state"|"derived"} rune + */ + function check_rune_binding(rune) { + const has_rune_binding = state.scope.get(rune); + if (has_rune_binding) { + throw new MigrationError( + `can't migrate \`$: ${state.str.original.substring(/** @type {number} */ (node.body.start), node.body.end)}\` to \`$${rune}\` because there's a variable named ${rune}.\n Rename the variable and try again or migrate by hand.` + ); + } + } + if ( node.body.type === 'ExpressionStatement' && node.body.expression.type === 'AssignmentExpression' @@ -878,6 +925,8 @@ const instance_script = { node.body.expression.right ); + check_rune_binding('derived'); + // $derived state.str.update( /** @type {number} */ (node.start), @@ -902,6 +951,7 @@ const instance_script = { } else { for (const binding of reassigned_bindings) { if (binding && (ids.includes(binding.node) || expression_ids.length === 0)) { + check_rune_binding('state'); const init = binding.kind === 'state' ? ' = $state()' diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$bindable-bindable-var-1/_config.js b/packages/svelte/tests/migrate/samples/impossible-migrate-$bindable-bindable-var-1/_config.js new file mode 100644 index 0000000000..aceae9ce69 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$bindable-bindable-var-1/_config.js @@ -0,0 +1,8 @@ +import { test } from '../../test'; + +export default test({ + logs: [ + 'One or more `@migration-task` comments were added to `output.svelte`, please check them and complete the migration manually.' + ], + errors: [] +}); diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$bindable-bindable-var-1/input.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$bindable-bindable-var-1/input.svelte new file mode 100644 index 0000000000..e66dfea9f7 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$bindable-bindable-var-1/input.svelte @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$bindable-bindable-var-1/output.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$bindable-bindable-var-1/output.svelte new file mode 100644 index 0000000000..f956c2cd75 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$bindable-bindable-var-1/output.svelte @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-1/_config.js b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-1/_config.js new file mode 100644 index 0000000000..aceae9ce69 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-1/_config.js @@ -0,0 +1,8 @@ +import { test } from '../../test'; + +export default test({ + logs: [ + 'One or more `@migration-task` comments were added to `output.svelte`, please check them and complete the migration manually.' + ], + errors: [] +}); diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-1/input.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-1/input.svelte new file mode 100644 index 0000000000..f8bde54909 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-1/input.svelte @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-1/output.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-1/output.svelte new file mode 100644 index 0000000000..64c61be00c --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-1/output.svelte @@ -0,0 +1,11 @@ + + + + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-2/_config.js b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-2/_config.js new file mode 100644 index 0000000000..aceae9ce69 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-2/_config.js @@ -0,0 +1,8 @@ +import { test } from '../../test'; + +export default test({ + logs: [ + 'One or more `@migration-task` comments were added to `output.svelte`, please check them and complete the migration manually.' + ], + errors: [] +}); diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-2/input.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-2/input.svelte new file mode 100644 index 0000000000..0b298cc7d2 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-2/input.svelte @@ -0,0 +1,5 @@ + + + diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-2/output.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-2/output.svelte new file mode 100644 index 0000000000..01864be058 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-2/output.svelte @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/_config.js b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/_config.js new file mode 100644 index 0000000000..aceae9ce69 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/_config.js @@ -0,0 +1,8 @@ +import { test } from '../../test'; + +export default test({ + logs: [ + 'One or more `@migration-task` comments were added to `output.svelte`, please check them and complete the migration manually.' + ], + errors: [] +}); diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/input.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/input.svelte new file mode 100644 index 0000000000..9203fdf30c --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/input.svelte @@ -0,0 +1,6 @@ + + + + \ No newline at end of file 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 new file mode 100644 index 0000000000..9e4f086aed --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/output.svelte @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-4/_config.js b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-4/_config.js new file mode 100644 index 0000000000..aceae9ce69 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-4/_config.js @@ -0,0 +1,8 @@ +import { test } from '../../test'; + +export default test({ + logs: [ + 'One or more `@migration-task` comments were added to `output.svelte`, please check them and complete the migration manually.' + ], + errors: [] +}); diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-4/input.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-4/input.svelte new file mode 100644 index 0000000000..c4179c7185 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-4/input.svelte @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-4/output.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-4/output.svelte new file mode 100644 index 0000000000..05cd363682 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-4/output.svelte @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$props-props-var-1/_config.js b/packages/svelte/tests/migrate/samples/impossible-migrate-$props-props-var-1/_config.js new file mode 100644 index 0000000000..aceae9ce69 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$props-props-var-1/_config.js @@ -0,0 +1,8 @@ +import { test } from '../../test'; + +export default test({ + logs: [ + 'One or more `@migration-task` comments were added to `output.svelte`, please check them and complete the migration manually.' + ], + errors: [] +}); diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$props-props-var-1/input.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$props-props-var-1/input.svelte new file mode 100644 index 0000000000..bc50d58776 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$props-props-var-1/input.svelte @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$props-props-var-1/output.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$props-props-var-1/output.svelte new file mode 100644 index 0000000000..33532dbaf5 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$props-props-var-1/output.svelte @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-1/_config.js b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-1/_config.js new file mode 100644 index 0000000000..aceae9ce69 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-1/_config.js @@ -0,0 +1,8 @@ +import { test } from '../../test'; + +export default test({ + logs: [ + 'One or more `@migration-task` comments were added to `output.svelte`, please check them and complete the migration manually.' + ], + errors: [] +}); diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-1/input.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-1/input.svelte new file mode 100644 index 0000000000..8999063751 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-1/input.svelte @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-1/output.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-1/output.svelte new file mode 100644 index 0000000000..d795f588ec --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-1/output.svelte @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-2/_config.js b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-2/_config.js new file mode 100644 index 0000000000..aceae9ce69 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-2/_config.js @@ -0,0 +1,8 @@ +import { test } from '../../test'; + +export default test({ + logs: [ + 'One or more `@migration-task` comments were added to `output.svelte`, please check them and complete the migration manually.' + ], + errors: [] +}); diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-2/input.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-2/input.svelte new file mode 100644 index 0000000000..354110f521 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-2/input.svelte @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-2/output.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-2/output.svelte new file mode 100644 index 0000000000..cf084c0364 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-2/output.svelte @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-3/_config.js b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-3/_config.js new file mode 100644 index 0000000000..aceae9ce69 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-3/_config.js @@ -0,0 +1,8 @@ +import { test } from '../../test'; + +export default test({ + logs: [ + 'One or more `@migration-task` comments were added to `output.svelte`, please check them and complete the migration manually.' + ], + errors: [] +}); diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-3/input.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-3/input.svelte new file mode 100644 index 0000000000..ddf64172fe --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-3/input.svelte @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-3/output.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-3/output.svelte new file mode 100644 index 0000000000..3e41b632d1 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-3/output.svelte @@ -0,0 +1,9 @@ + + + + \ No newline at end of file From 31e6bbb6466d1a8dad71efeb9f63b7e1c1010d32 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Fri, 8 Nov 2024 12:25:03 +0100 Subject: [PATCH 137/404] fix: add `lang="ts"` attribute during migration if needed (#14222) * fix: add `lang="ts"` attribute during migration if needed fixes #14219 * fix --- .changeset/seven-clocks-pull.md | 5 +++++ packages/svelte/src/compiler/migrate/index.js | 10 +++++++++- .../tests/migrate/samples/slot-use_ts-2/_config.js | 5 +++++ .../migrate/samples/slot-use_ts-2/input.svelte | 5 +++++ .../migrate/samples/slot-use_ts-2/output.svelte | 10 ++++++++++ .../tests/migrate/samples/slot-use_ts-3/_config.js | 5 +++++ .../migrate/samples/slot-use_ts-3/input.svelte | 6 ++++++ .../migrate/samples/slot-use_ts-3/output.svelte | 13 +++++++++++++ .../tests/migrate/samples/slot-use_ts/output.svelte | 2 +- 9 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 .changeset/seven-clocks-pull.md create mode 100644 packages/svelte/tests/migrate/samples/slot-use_ts-2/_config.js create mode 100644 packages/svelte/tests/migrate/samples/slot-use_ts-2/input.svelte create mode 100644 packages/svelte/tests/migrate/samples/slot-use_ts-2/output.svelte create mode 100644 packages/svelte/tests/migrate/samples/slot-use_ts-3/_config.js create mode 100644 packages/svelte/tests/migrate/samples/slot-use_ts-3/input.svelte create mode 100644 packages/svelte/tests/migrate/samples/slot-use_ts-3/output.svelte diff --git a/.changeset/seven-clocks-pull.md b/.changeset/seven-clocks-pull.md new file mode 100644 index 0000000000..e426560b7b --- /dev/null +++ b/.changeset/seven-clocks-pull.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: add `lang="ts"` attribute during migration if needed diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index b7687af09f..5bcf160e8b 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -207,8 +207,12 @@ export function migrate(source, { filename, use_ts } = {}) { analysis.uses_props || state.has_svelte_self; + const need_ts_tag = + state.uses_ts && + (!parsed.instance || !parsed.instance.attributes.some((attr) => attr.name === 'lang')); + if (!parsed.instance && need_script) { - str.appendRight(0, ' + + diff --git a/packages/svelte/tests/migrate/samples/slot-use_ts-2/output.svelte b/packages/svelte/tests/migrate/samples/slot-use_ts-2/output.svelte new file mode 100644 index 0000000000..a8e8db66b1 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/slot-use_ts-2/output.svelte @@ -0,0 +1,10 @@ + + +{@render children?.()} \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/slot-use_ts-3/_config.js b/packages/svelte/tests/migrate/samples/slot-use_ts-3/_config.js new file mode 100644 index 0000000000..dbc620d66e --- /dev/null +++ b/packages/svelte/tests/migrate/samples/slot-use_ts-3/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + use_ts: true +}); diff --git a/packages/svelte/tests/migrate/samples/slot-use_ts-3/input.svelte b/packages/svelte/tests/migrate/samples/slot-use_ts-3/input.svelte new file mode 100644 index 0000000000..b228fef106 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/slot-use_ts-3/input.svelte @@ -0,0 +1,6 @@ + + + diff --git a/packages/svelte/tests/migrate/samples/slot-use_ts-3/output.svelte b/packages/svelte/tests/migrate/samples/slot-use_ts-3/output.svelte new file mode 100644 index 0000000000..ab95eace3a --- /dev/null +++ b/packages/svelte/tests/migrate/samples/slot-use_ts-3/output.svelte @@ -0,0 +1,13 @@ + + +{@render children?.()} \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/slot-use_ts/output.svelte b/packages/svelte/tests/migrate/samples/slot-use_ts/output.svelte index 3e92f5ceb1..0ba0bf53e9 100644 --- a/packages/svelte/tests/migrate/samples/slot-use_ts/output.svelte +++ b/packages/svelte/tests/migrate/samples/slot-use_ts/output.svelte @@ -1,4 +1,4 @@ - @@ -30,4 +32,18 @@ {/snippet} + + + + {#snippet children({ args })} + {@render children_render?.()} + {/snippet} + + + +
+ {#snippet children({ args })} + {@render children_render?.()} + {/snippet} +
\ No newline at end of file From 1060bea6e1fd1a02f2d251e200591864511e2ea2 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Fri, 8 Nov 2024 15:30:38 +0100 Subject: [PATCH 139/404] fix: consider `valueOf` in the reactive methods of `SvelteDate` (#14227) --- .changeset/cyan-cooks-nail.md | 5 +++++ packages/svelte/src/reactivity/date.js | 2 +- packages/svelte/src/reactivity/date.test.ts | 24 +++++++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 .changeset/cyan-cooks-nail.md diff --git a/.changeset/cyan-cooks-nail.md b/.changeset/cyan-cooks-nail.md new file mode 100644 index 0000000000..656cf3a413 --- /dev/null +++ b/.changeset/cyan-cooks-nail.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: consider `valueOf` in the reactive methods of `SvelteDate` diff --git a/packages/svelte/src/reactivity/date.js b/packages/svelte/src/reactivity/date.js index 9eb0cc3521..2d8de624bc 100644 --- a/packages/svelte/src/reactivity/date.js +++ b/packages/svelte/src/reactivity/date.js @@ -30,7 +30,7 @@ export class SvelteDate extends Date { ); for (const method of methods) { - if (method.startsWith('get') || method.startsWith('to')) { + if (method.startsWith('get') || method.startsWith('to') || method === 'valueOf') { // @ts-ignore proto[method] = function (...args) { // don't memoize if there are arguments diff --git a/packages/svelte/src/reactivity/date.test.ts b/packages/svelte/src/reactivity/date.test.ts index d07b6780c6..87bfde41c8 100644 --- a/packages/svelte/src/reactivity/date.test.ts +++ b/packages/svelte/src/reactivity/date.test.ts @@ -588,6 +588,30 @@ test('Date.toLocaleString', () => { cleanup(); }); +test('Date.valueOf', () => { + const date = new SvelteDate(initial_date); + + const log: any = []; + + const cleanup = effect_root(() => { + render_effect(() => { + log.push(date.valueOf()); + }); + }); + + flushSync(); + + assert.deepEqual(log, [initial_date.valueOf()]); + + flushSync(() => { + date.setTime(date.getTime() + 10); + }); + + assert.deepEqual(log, [initial_date.valueOf(), new Date(initial_date.getTime() + 10).valueOf()]); + + cleanup(); +}); + test('Date.instanceOf', () => { assert.equal(new SvelteDate() instanceof Date, true); }); From 4bcd01b9941c7ba393738b5e038d9f4ad96b3bbd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 8 Nov 2024 11:48:03 -0800 Subject: [PATCH 140/404] Version Packages (#14207) Co-authored-by: github-actions[bot] --- .changeset/chatty-gorillas-poke.md | 5 ----- .changeset/cyan-cooks-nail.md | 5 ----- .changeset/four-jobs-care.md | 5 ----- .changeset/giant-waves-act.md | 5 ----- .changeset/late-dodos-film.md | 5 ----- .changeset/lazy-chefs-float.md | 5 ----- .changeset/nice-numbers-remember.md | 5 ----- .changeset/seven-clocks-pull.md | 5 ----- packages/svelte/CHANGELOG.md | 20 ++++++++++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 11 files changed, 22 insertions(+), 42 deletions(-) delete mode 100644 .changeset/chatty-gorillas-poke.md delete mode 100644 .changeset/cyan-cooks-nail.md delete mode 100644 .changeset/four-jobs-care.md delete mode 100644 .changeset/giant-waves-act.md delete mode 100644 .changeset/late-dodos-film.md delete mode 100644 .changeset/lazy-chefs-float.md delete mode 100644 .changeset/nice-numbers-remember.md delete mode 100644 .changeset/seven-clocks-pull.md diff --git a/.changeset/chatty-gorillas-poke.md b/.changeset/chatty-gorillas-poke.md deleted file mode 100644 index 744adcf214..0000000000 --- a/.changeset/chatty-gorillas-poke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: add migration task when there's a variable named that would conflict with a rune diff --git a/.changeset/cyan-cooks-nail.md b/.changeset/cyan-cooks-nail.md deleted file mode 100644 index 656cf3a413..0000000000 --- a/.changeset/cyan-cooks-nail.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: consider `valueOf` in the reactive methods of `SvelteDate` diff --git a/.changeset/four-jobs-care.md b/.changeset/four-jobs-care.md deleted file mode 100644 index 51e5d22313..0000000000 --- a/.changeset/four-jobs-care.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: handle sibling combinators within `:has` diff --git a/.changeset/giant-waves-act.md b/.changeset/giant-waves-act.md deleted file mode 100644 index 591f0f25ba..0000000000 --- a/.changeset/giant-waves-act.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: consider variables with synthetic store sub as state diff --git a/.changeset/late-dodos-film.md b/.changeset/late-dodos-film.md deleted file mode 100644 index c3636019da..0000000000 --- a/.changeset/late-dodos-film.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: read index as a source in legacy keyed each block diff --git a/.changeset/lazy-chefs-float.md b/.changeset/lazy-chefs-float.md deleted file mode 100644 index ce15e3dabc..0000000000 --- a/.changeset/lazy-chefs-float.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: account for shadowing children slot during migration diff --git a/.changeset/nice-numbers-remember.md b/.changeset/nice-numbers-remember.md deleted file mode 100644 index 810343aedf..0000000000 --- a/.changeset/nice-numbers-remember.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure explicit nesting selector is always applied diff --git a/.changeset/seven-clocks-pull.md b/.changeset/seven-clocks-pull.md deleted file mode 100644 index e426560b7b..0000000000 --- a/.changeset/seven-clocks-pull.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: add `lang="ts"` attribute during migration if needed diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 4283f406d6..98fdbc6937 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,25 @@ # svelte +## 5.1.13 + +### Patch Changes + +- fix: add migration task when there's a variable named that would conflict with a rune ([#14216](https://github.com/sveltejs/svelte/pull/14216)) + +- fix: consider `valueOf` in the reactive methods of `SvelteDate` ([#14227](https://github.com/sveltejs/svelte/pull/14227)) + +- fix: handle sibling combinators within `:has` ([#14213](https://github.com/sveltejs/svelte/pull/14213)) + +- fix: consider variables with synthetic store sub as state ([#14195](https://github.com/sveltejs/svelte/pull/14195)) + +- fix: read index as a source in legacy keyed each block ([#14208](https://github.com/sveltejs/svelte/pull/14208)) + +- fix: account for shadowing children slot during migration ([#14224](https://github.com/sveltejs/svelte/pull/14224)) + +- fix: ensure explicit nesting selector is always applied ([#14193](https://github.com/sveltejs/svelte/pull/14193)) + +- fix: add `lang="ts"` attribute during migration if needed ([#14222](https://github.com/sveltejs/svelte/pull/14222)) + ## 5.1.12 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 0b0eb32ffd..9b0c03a5af 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.1.12", + "version": "5.1.13", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 9c883457e8..c50265e998 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.1.12'; +export const VERSION = '5.1.13'; export const PUBLIC_VERSION = '5'; From 6534742ae13b6f064f0768df245b3d363add0e1e Mon Sep 17 00:00:00 2001 From: CJSnyman <98422173+CJSnyman@users.noreply.github.com> Date: Sun, 10 Nov 2024 15:25:11 +0200 Subject: [PATCH 141/404] docs: fix grammar (#14244) Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> --- documentation/docs/07-misc/02-testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/07-misc/02-testing.md b/documentation/docs/07-misc/02-testing.md index 1c3d4ba6e1..c8774e341f 100644 --- a/documentation/docs/07-misc/02-testing.md +++ b/documentation/docs/07-misc/02-testing.md @@ -197,7 +197,7 @@ 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 start with Playwright, either let you guide by [their 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`. +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`. 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: From 1e5a385c968afb302beb38e7d8dd2982fdd66d0c Mon Sep 17 00:00:00 2001 From: Hosam Sultan Date: Sun, 10 Nov 2024 05:31:50 -0800 Subject: [PATCH 142/404] docs: fix typo (#14232) --- documentation/docs/03-template-syntax/06-snippet.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/03-template-syntax/06-snippet.md b/documentation/docs/03-template-syntax/06-snippet.md index d4ec5a0cef..4bbc970c5d 100644 --- a/documentation/docs/03-template-syntax/06-snippet.md +++ b/documentation/docs/03-template-syntax/06-snippet.md @@ -169,7 +169,7 @@ Any content inside the component tags that is _not_ a snippet declaration implic ```svelte - ``` ```svelte From d207666ec3066a7fed208141c31e06fcf3275059 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Sun, 10 Nov 2024 17:48:10 +0100 Subject: [PATCH 143/404] fix: consider img with loading attribute not static (#14237) * fix: consider img with loading attribute not static * chore: add comment for `is_static_element` * chore: better comment Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> --------- Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> --- .changeset/nice-chicken-wonder.md | 5 +++++ .../phases/3-transform/client/visitors/shared/fragment.js | 5 +++++ .../samples/img-loading-lazy-no-static/_config.js | 5 +++++ .../samples/img-loading-lazy-no-static/main.svelte | 2 ++ 4 files changed, 17 insertions(+) create mode 100644 .changeset/nice-chicken-wonder.md create mode 100644 packages/svelte/tests/runtime-runes/samples/img-loading-lazy-no-static/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/img-loading-lazy-no-static/main.svelte diff --git a/.changeset/nice-chicken-wonder.md b/.changeset/nice-chicken-wonder.md new file mode 100644 index 0000000000..b9f6e39fb8 --- /dev/null +++ b/.changeset/nice-chicken-wonder.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: consider img with loading attribute not 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 022b8574d9..98272817df 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 @@ -150,6 +150,11 @@ function is_static_element(node) { return false; } + // We need to apply src and loading after appending the img to the DOM for lazy loading to work + if (node.name === 'img' && attribute.name === 'loading') { + return false; + } + if (node.name.includes('-')) { return false; // we're setting all attributes on custom elements through properties } diff --git a/packages/svelte/tests/runtime-runes/samples/img-loading-lazy-no-static/_config.js b/packages/svelte/tests/runtime-runes/samples/img-loading-lazy-no-static/_config.js new file mode 100644 index 0000000000..673ff2d754 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/img-loading-lazy-no-static/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + html: `

` +}); diff --git a/packages/svelte/tests/runtime-runes/samples/img-loading-lazy-no-static/main.svelte b/packages/svelte/tests/runtime-runes/samples/img-loading-lazy-no-static/main.svelte new file mode 100644 index 0000000000..15425d9051 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/img-loading-lazy-no-static/main.svelte @@ -0,0 +1,2 @@ +

+ \ No newline at end of file From 7bc94b998476e9dfed73ad9ef0f66b7d06252b27 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Sun, 10 Nov 2024 20:55:39 +0100 Subject: [PATCH 144/404] fix: do not treat reassigned synthetic binds as state in runes mode (#14236) * fix: do not treat reassigned synthetic binds as state in runes mode * fix: avoid calling checks if we are in runes mode --- .changeset/bright-papayas-sneeze.md | 5 +++ .../src/compiler/phases/2-analyze/index.js | 33 +++++++++++++------ .../reassigned-store-not-state/_config.js | 7 ++++ .../reassigned-store-not-state/main.svelte | 13 ++++++++ 4 files changed, 48 insertions(+), 10 deletions(-) create mode 100644 .changeset/bright-papayas-sneeze.md create mode 100644 packages/svelte/tests/runtime-runes/samples/reassigned-store-not-state/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/reassigned-store-not-state/main.svelte diff --git a/.changeset/bright-papayas-sneeze.md b/.changeset/bright-papayas-sneeze.md new file mode 100644 index 0000000000..8587aebc66 --- /dev/null +++ b/.changeset/bright-papayas-sneeze.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: do not treat reassigned synthetic binds as state in runes mode diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 36d8145145..9e4f72a66d 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -276,6 +276,8 @@ export function analyze_component(root, source, options) { /** @type {Template} */ const template = { ast: root.fragment, scope, scopes }; + let synthetic_stores_legacy_check = []; + // create synthetic bindings for store subscriptions for (const [name, references] of module.scope.references) { if (name[0] !== '$' || RESERVED.includes(name)) continue; @@ -351,16 +353,21 @@ export function analyze_component(root, source, options) { } } - // if we are creating a synthetic binding for a let declaration we should also declare - // the declaration as state in case it's reassigned - if ( - declaration !== null && - declaration.kind === 'normal' && - declaration.declaration_kind === 'let' && - declaration.reassigned - ) { - declaration.kind = 'state'; - } + // we push to the array because at this moment in time we can't be sure if we are in legacy + // mode yet because we are still changing the module scope + synthetic_stores_legacy_check.push(() => { + // if we are creating a synthetic binding for a let declaration we should also declare + // the declaration as state in case it's reassigned and we are not in runes mode (the function will + // not be called if we are not in runes mode, that's why there's no !runes check here) + if ( + declaration !== null && + declaration.kind === 'normal' && + declaration.declaration_kind === 'let' && + declaration.reassigned + ) { + declaration.kind = 'state'; + } + }); const binding = instance.scope.declare(b.id(name), 'store_sub', 'synthetic'); binding.references = references; @@ -373,6 +380,12 @@ export function analyze_component(root, source, options) { const runes = options.runes ?? Array.from(module.scope.references.keys()).some(is_rune); + if (!runes) { + for (let check of synthetic_stores_legacy_check) { + check(); + } + } + if (runes && root.module) { const context = root.module.attributes.find((attribute) => attribute.name === 'context'); if (context) { diff --git a/packages/svelte/tests/runtime-runes/samples/reassigned-store-not-state/_config.js b/packages/svelte/tests/runtime-runes/samples/reassigned-store-not-state/_config.js new file mode 100644 index 0000000000..7091b34479 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/reassigned-store-not-state/_config.js @@ -0,0 +1,7 @@ +import { test } from '../../test'; + +export default test({ + async test({ logs, assert }) { + assert.deepEqual(logs, ['world']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/reassigned-store-not-state/main.svelte b/packages/svelte/tests/runtime-runes/samples/reassigned-store-not-state/main.svelte new file mode 100644 index 0000000000..95da959012 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/reassigned-store-not-state/main.svelte @@ -0,0 +1,13 @@ + From ae9f53a3bde79e8777e865acac3b99612c2cec71 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Sun, 10 Nov 2024 20:56:00 +0100 Subject: [PATCH 145/404] fix: account for mutations in script module in ownership check (#14253) --- .changeset/good-laws-sin.md | 5 +++++ packages/svelte/src/internal/client/dev/ownership.js | 3 +++ .../samples/mutate-state-in-context-dev/_config.js | 7 +++++++ .../samples/mutate-state-in-context-dev/main.svelte | 6 ++++++ 4 files changed, 21 insertions(+) create mode 100644 .changeset/good-laws-sin.md create mode 100644 packages/svelte/tests/runtime-runes/samples/mutate-state-in-context-dev/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/mutate-state-in-context-dev/main.svelte diff --git a/.changeset/good-laws-sin.md b/.changeset/good-laws-sin.md new file mode 100644 index 0000000000..38d3d3bfc8 --- /dev/null +++ b/.changeset/good-laws-sin.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: account for mutations in script module in ownership check diff --git a/packages/svelte/src/internal/client/dev/ownership.js b/packages/svelte/src/internal/client/dev/ownership.js index 64f424e1a9..d113d9ae90 100644 --- a/packages/svelte/src/internal/client/dev/ownership.js +++ b/packages/svelte/src/internal/client/dev/ownership.js @@ -59,6 +59,9 @@ export function get_component() { } 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; } diff --git a/packages/svelte/tests/runtime-runes/samples/mutate-state-in-context-dev/_config.js b/packages/svelte/tests/runtime-runes/samples/mutate-state-in-context-dev/_config.js new file mode 100644 index 0000000000..ed0ead960b --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/mutate-state-in-context-dev/_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/mutate-state-in-context-dev/main.svelte b/packages/svelte/tests/runtime-runes/samples/mutate-state-in-context-dev/main.svelte new file mode 100644 index 0000000000..ff6f816912 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/mutate-state-in-context-dev/main.svelte @@ -0,0 +1,6 @@ + + +

Values: {JSON.stringify(obj)}

\ No newline at end of file From 653f4accbd3393fdaed072b936a39d908cf9d20a Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Mon, 11 Nov 2024 10:28:56 +0100 Subject: [PATCH 146/404] fix: migration script messing with attributes (#14260) --- .changeset/afraid-donuts-whisper.md | 5 +++++ packages/svelte/src/compiler/migrate/index.js | 2 +- .../slot-dont-mess-with-attributes/input.svelte | 5 +++++ .../slot-dont-mess-with-attributes/output.svelte | 15 +++++++++++++++ 4 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 .changeset/afraid-donuts-whisper.md create mode 100644 packages/svelte/tests/migrate/samples/slot-dont-mess-with-attributes/input.svelte create mode 100644 packages/svelte/tests/migrate/samples/slot-dont-mess-with-attributes/output.svelte diff --git a/.changeset/afraid-donuts-whisper.md b/.changeset/afraid-donuts-whisper.md new file mode 100644 index 0000000000..df9eea40ad --- /dev/null +++ b/.changeset/afraid-donuts-whisper.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: migration script messing with attributes diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index 8c2429b1c9..080babdfb7 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -1309,7 +1309,7 @@ const template = { parent.type === 'RegularElement' || parent.type === 'SvelteElement' || parent.type === 'SvelteFragment') && - parent.attributes.some((attr) => (attr.type = 'LetDirective')) + parent.attributes.some((attr) => attr.type === 'LetDirective') ) ) { aliased_slot_name = `${name}_render`; diff --git a/packages/svelte/tests/migrate/samples/slot-dont-mess-with-attributes/input.svelte b/packages/svelte/tests/migrate/samples/slot-dont-mess-with-attributes/input.svelte new file mode 100644 index 0000000000..53f38e0332 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/slot-dont-mess-with-attributes/input.svelte @@ -0,0 +1,5 @@ + + + diff --git a/packages/svelte/tests/migrate/samples/slot-dont-mess-with-attributes/output.svelte b/packages/svelte/tests/migrate/samples/slot-dont-mess-with-attributes/output.svelte new file mode 100644 index 0000000000..39a6ff7957 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/slot-dont-mess-with-attributes/output.svelte @@ -0,0 +1,15 @@ + + + + {@render children?.()} + \ No newline at end of file From 832499fee889e7e88911649c64ddbb84a2a8b454 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 10:36:28 +0100 Subject: [PATCH 147/404] Version Packages (#14251) Co-authored-by: github-actions[bot] --- .changeset/afraid-donuts-whisper.md | 5 ----- .changeset/bright-papayas-sneeze.md | 5 ----- .changeset/good-laws-sin.md | 5 ----- .changeset/nice-chicken-wonder.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/afraid-donuts-whisper.md delete mode 100644 .changeset/bright-papayas-sneeze.md delete mode 100644 .changeset/good-laws-sin.md delete mode 100644 .changeset/nice-chicken-wonder.md diff --git a/.changeset/afraid-donuts-whisper.md b/.changeset/afraid-donuts-whisper.md deleted file mode 100644 index df9eea40ad..0000000000 --- a/.changeset/afraid-donuts-whisper.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: migration script messing with attributes diff --git a/.changeset/bright-papayas-sneeze.md b/.changeset/bright-papayas-sneeze.md deleted file mode 100644 index 8587aebc66..0000000000 --- a/.changeset/bright-papayas-sneeze.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: do not treat reassigned synthetic binds as state in runes mode diff --git a/.changeset/good-laws-sin.md b/.changeset/good-laws-sin.md deleted file mode 100644 index 38d3d3bfc8..0000000000 --- a/.changeset/good-laws-sin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: account for mutations in script module in ownership check diff --git a/.changeset/nice-chicken-wonder.md b/.changeset/nice-chicken-wonder.md deleted file mode 100644 index b9f6e39fb8..0000000000 --- a/.changeset/nice-chicken-wonder.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: consider img with loading attribute not static diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 98fdbc6937..9ab2e67282 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,17 @@ # svelte +## 5.1.14 + +### Patch Changes + +- fix: migration script messing with attributes ([#14260](https://github.com/sveltejs/svelte/pull/14260)) + +- fix: do not treat reassigned synthetic binds as state in runes mode ([#14236](https://github.com/sveltejs/svelte/pull/14236)) + +- fix: account for mutations in script module in ownership check ([#14253](https://github.com/sveltejs/svelte/pull/14253)) + +- fix: consider img with loading attribute not static ([#14237](https://github.com/sveltejs/svelte/pull/14237)) + ## 5.1.13 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 9b0c03a5af..04efd21643 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.1.13", + "version": "5.1.14", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index c50265e998..17232bc9d6 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.1.13'; +export const VERSION = '5.1.14'; export const PUBLIC_VERSION = '5'; From 0fa43e216dc0744f5b41751f087024c45efde66f Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Mon, 11 Nov 2024 14:55:04 +0100 Subject: [PATCH 148/404] fix: consider static attributes that are inlined in the template (#14249) * fix: consider static attributes that are inlined in the template * fix: use `is_inlinable_expression` * fix: move check for inlinable expression as last * fix: simplify and correct * chore: accept single node in `is_inlinable_expression` * chore: update comment * chore: add snapshots for non static nodes --- .changeset/mighty-ads-appear.md | 5 +++ .../phases/3-transform/client/utils.js | 27 +++++++++++++- .../client/visitors/RegularElement.js | 36 ++++--------------- .../client/visitors/shared/fragment.js | 21 ++++++----- .../_expected/client/index.svelte.js | 5 +-- .../_expected/client/index.svelte.js | 24 ++++++++++++- .../_expected/server/index.svelte.js | 2 +- .../samples/skip-static-subtree/index.svelte | 15 ++++++++ 8 files changed, 91 insertions(+), 44 deletions(-) create mode 100644 .changeset/mighty-ads-appear.md diff --git a/.changeset/mighty-ads-appear.md b/.changeset/mighty-ads-appear.md new file mode 100644 index 0000000000..c1b3b6a746 --- /dev/null +++ b/.changeset/mighty-ads-appear.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: consider static attributes that are inlined in the template 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 2647f0a3be..c460905977 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/utils.js @@ -1,5 +1,5 @@ /** @import { ArrowFunctionExpression, Expression, FunctionDeclaration, FunctionExpression, Identifier, Pattern, PrivateIdentifier, Statement } from 'estree' */ -/** @import { Binding, SvelteNode } from '#compiler' */ +/** @import { AST, Binding, SvelteNode } from '#compiler' */ /** @import { ClientTransformState, ComponentClientTransformState, ComponentContext } from './types.js' */ /** @import { Analysis } from '../../types.js' */ /** @import { Scope } from '../../scope.js' */ @@ -326,3 +326,28 @@ export function can_inline_variable(binding) { binding.initial?.type === 'Literal' ); } + +/** + * @param {(AST.Text | AST.ExpressionTag) | (AST.Text | AST.ExpressionTag)[]} node_or_nodes + * @param {import('./types.js').ComponentClientTransformState} state + */ +export function is_inlinable_expression(node_or_nodes, state) { + let nodes = Array.isArray(node_or_nodes) ? node_or_nodes : [node_or_nodes]; + let has_expression_tag = false; + for (let value of nodes) { + if (value.type === 'ExpressionTag') { + if (value.expression.type === 'Identifier') { + const binding = state.scope + .owner(value.expression.name) + ?.declarations.get(value.expression.name); + if (!can_inline_variable(binding)) { + return false; + } + } else { + return false; + } + has_expression_tag = true; + } + } + return has_expression_tag; +} 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 dafe1e74ae..4d3cebcee6 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 @@ -19,7 +19,12 @@ import { 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, can_inline_variable, create_derived } from '../utils.js'; +import { + build_getter, + can_inline_variable, + create_derived, + is_inlinable_expression +} from '../utils.js'; import { get_attribute_name, build_attribute_value, @@ -584,10 +589,7 @@ function build_element_attribute_update_assignment(element, node_id, attribute, const inlinable_expression = attribute.value === true ? false // not an expression - : is_inlinable_expression( - Array.isArray(attribute.value) ? attribute.value : [attribute.value], - context.state - ); + : is_inlinable_expression(attribute.value, context.state); if (attribute.metadata.expression.has_state) { if (has_call) { state.init.push(build_update(update)); @@ -605,30 +607,6 @@ function build_element_attribute_update_assignment(element, node_id, attribute, } } -/** - * @param {(AST.Text | AST.ExpressionTag)[]} nodes - * @param {import('../types.js').ComponentClientTransformState} state - */ -function is_inlinable_expression(nodes, state) { - let has_expression_tag = false; - for (let value of nodes) { - if (value.type === 'ExpressionTag') { - if (value.expression.type === 'Identifier') { - const binding = state.scope - .owner(value.expression.name) - ?.declarations.get(value.expression.name); - if (!can_inline_variable(binding)) { - return false; - } - } else { - return false; - } - has_expression_tag = true; - } - } - return has_expression_tag; -} - /** * Like `build_element_attribute_update_assignment` but without any special attribute treatment. * @param {Identifier} node_id 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 98272817df..3b009fb5b5 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,6 +3,7 @@ /** @import { ComponentContext } from '../../types' */ import { is_event_attribute, is_text_attribute } from '../../../../../utils/ast.js'; import * as b from '../../../../../utils/builders.js'; +import { is_inlinable_expression } from '../../utils.js'; import { build_template_literal, build_update } from './utils.js'; /** @@ -97,7 +98,7 @@ export function process_children(nodes, initial, is_element, { visit, state }) { let child_state = state; - if (is_static_element(node)) { + if (is_static_element(node, state)) { skipped += 1; } else if (node.type === 'EachBlock' && nodes.length === 1 && is_element) { node.metadata.is_controlled = true; @@ -124,10 +125,12 @@ export function process_children(nodes, initial, is_element, { visit, state }) { /** * @param {SvelteNode} node + * @param {ComponentContext["state"]} state */ -function is_static_element(node) { +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 for (const attribute of node.attributes) { if (attribute.type !== 'Attribute') { @@ -138,10 +141,6 @@ function is_static_element(node) { return false; } - if (attribute.value !== true && !is_text_attribute(attribute)) { - return false; - } - if (attribute.name === 'autofocus' || attribute.name === 'muted') { return false; } @@ -155,8 +154,14 @@ function is_static_element(node) { return false; } - if (node.name.includes('-')) { - return false; // we're setting all attributes on custom elements through properties + if ( + attribute.value !== true && + !is_text_attribute(attribute) && + // If the attribute is not a text attribute but is inlinable we will directly inline it in the + // the template so before returning false we need to check that the attribute is not inlinable + !is_inlinable_expression(attribute.value, state) + ) { + return false; } } diff --git a/packages/svelte/tests/snapshot/samples/inline-module-vars/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/inline-module-vars/_expected/client/index.svelte.js index 69c6901d50..46ed3c1913 100644 --- a/packages/svelte/tests/snapshot/samples/inline-module-vars/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/inline-module-vars/_expected/client/index.svelte.js @@ -9,11 +9,8 @@ var root = $.template(`

we don't need to traverse these nodes

or

these

ones

these

trailing

nodes

can

be

completely

ignored

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

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(); @@ -22,6 +22,28 @@ export default function Skip_static_subtree($$anchor, $$props) { $.set_custom_element_data(custom_elements, "with", "attributes"); $.reset(cant_skip); + + var div = $.sibling(cant_skip, 2); + var input = $.child(div); + + $.autofocus(input, true); + $.reset(div); + + var div_1 = $.sibling(div, 2); + var source = $.child(div_1); + + source.muted = true; + $.reset(div_1); + + var select = $.sibling(div_1, 2); + var option = $.child(select); + + option.value = null == (option.__value = "a") ? "" : "a"; + $.reset(select); + + var img = $.sibling(select, 2); + $.template_effect(() => $.set_text(text, $$props.title)); + $.handle_lazy_img(img); $.append($$anchor, fragment); } \ No newline at end of file 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 2bd910508d..d09ac657f9 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/skip-static-subtree/index.svelte b/packages/svelte/tests/snapshot/samples/skip-static-subtree/index.svelte index de399169b4..cb73eba707 100644 --- a/packages/svelte/tests/snapshot/samples/skip-static-subtree/index.svelte +++ b/packages/svelte/tests/snapshot/samples/skip-static-subtree/index.svelte @@ -30,3 +30,18 @@ + +
+ + +
+ +
+ +
+ + + + \ No newline at end of file From 4a85c4157df1bd5d8903b336bfd4af204d32f1ce Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 10:07:09 -0800 Subject: [PATCH 149/404] Version Packages (#14262) Co-authored-by: github-actions[bot] --- .changeset/mighty-ads-appear.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/mighty-ads-appear.md diff --git a/.changeset/mighty-ads-appear.md b/.changeset/mighty-ads-appear.md deleted file mode 100644 index c1b3b6a746..0000000000 --- a/.changeset/mighty-ads-appear.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: consider static attributes that are inlined in the template diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 9ab2e67282..e2c59c27f4 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.1.15 + +### Patch Changes + +- fix: consider static attributes that are inlined in the template ([#14249](https://github.com/sveltejs/svelte/pull/14249)) + ## 5.1.14 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 04efd21643..3124fd73af 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.1.14", + "version": "5.1.15", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 17232bc9d6..20a804671e 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.1.14'; +export const VERSION = '5.1.15'; export const PUBLIC_VERSION = '5'; From 36ece1c38114a7d1b4177829f76201eaeef7a6f4 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 12 Nov 2024 14:46:26 +0100 Subject: [PATCH 150/404] fix: don't wrap pseudo classes inside `:global(...)` with another `:global(...)` (#14267) fixes #14088 --- .changeset/hot-ladybugs-clap.md | 5 ++ packages/svelte/src/compiler/migrate/index.js | 46 ++++++++++++++----- .../samples/is-not-where-has/input.svelte | 2 + .../samples/is-not-where-has/output.svelte | 2 + 4 files changed, 44 insertions(+), 11 deletions(-) create mode 100644 .changeset/hot-ladybugs-clap.md diff --git a/.changeset/hot-ladybugs-clap.md b/.changeset/hot-ladybugs-clap.md new file mode 100644 index 0000000000..20acdd0750 --- /dev/null +++ b/.changeset/hot-ladybugs-clap.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: don't wrap pseudo classes inside `:global(...)` with another `:global(...)` during migration diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index 080babdfb7..4942dd7db2 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -40,9 +40,10 @@ class MigrationError extends Error { */ function migrate_css(state) { if (!state.analysis.css.ast?.start) return; - let code = state.str + const css_contents = state.str .snip(state.analysis.css.ast.start, /** @type {number} */ (state.analysis.css.ast?.end)) .toString(); + let code = css_contents; let starting = 0; // since we already blank css we can't work directly on `state.str` so we will create a copy that we can update @@ -56,23 +57,28 @@ function migrate_css(state) { ) { let start = code.indexOf('(') + 1; let is_global = false; + const global_str = ':global'; const next_global = code.indexOf(global_str); const str_between = code.substring(start, next_global); if (!str_between.trim()) { is_global = true; start += global_str.length; + } else { + const prev_global = css_contents.lastIndexOf(global_str, starting); + if (prev_global > -1) { + const end = + find_closing_parenthesis(css_contents.indexOf('(', prev_global) + 1, css_contents) - + starting; + if (end > start) { + starting += end; + code = code.substring(end); + continue; + } + } } - let parenthesis = 1; - let end = start; - let char = code[end]; - // find the closing parenthesis - while (parenthesis !== 0 && char) { - if (char === '(') parenthesis++; - if (char === ')') parenthesis--; - end++; - char = code[end]; - } + + const end = find_closing_parenthesis(start, code); if (start && end) { if (!is_global && !code.startsWith(':not')) { str.prependLeft(starting + start, ':global('); @@ -89,6 +95,24 @@ function migrate_css(state) { state.str.update(state.analysis.css.ast?.start, state.analysis.css.ast?.end, str.toString()); } +/** + * @param {number} start + * @param {string} code + */ +function find_closing_parenthesis(start, code) { + let parenthesis = 1; + let end = start; + let char = code[end]; + // find the closing parenthesis + while (parenthesis !== 0 && char) { + if (char === '(') parenthesis++; + if (char === ')') parenthesis--; + end++; + char = code[end]; + } + return end; +} + /** * 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. diff --git a/packages/svelte/tests/migrate/samples/is-not-where-has/input.svelte b/packages/svelte/tests/migrate/samples/is-not-where-has/input.svelte index fbfc1c119f..88c54ad2db 100644 --- a/packages/svelte/tests/migrate/samples/is-not-where-has/input.svelte +++ b/packages/svelte/tests/migrate/samples/is-not-where-has/input.svelte @@ -47,6 +47,8 @@ what if i'm talking about `:has()` in my blog? div :where(:global(.class:is(span:has(* > *)))){} div :is(:global(.class:is(span:is(:hover)), .x)){} + :global(button:has(.is-active)){} + div{ p:has(&){ diff --git a/packages/svelte/tests/migrate/samples/is-not-where-has/output.svelte b/packages/svelte/tests/migrate/samples/is-not-where-has/output.svelte index d8bb703ac5..9f23cb1024 100644 --- a/packages/svelte/tests/migrate/samples/is-not-where-has/output.svelte +++ b/packages/svelte/tests/migrate/samples/is-not-where-has/output.svelte @@ -47,6 +47,8 @@ what if i'm talking about `:has()` in my blog? div :where(:global(.class:is(span:has(* > *)))){} div :is(:global(.class:is(span:is(:hover)), .x)){} + :global(button:has(.is-active)){} + div{ p:has(:global(&)){ From 033061842d463c1db9a0ebed3b27a2ab5b7fddb7 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 12 Nov 2024 18:13:05 +0100 Subject: [PATCH 151/404] fix: named slots with reserved keywords during migration (#14278) Fixes named slots with a reserved keyword not working anymore after migration. Re-uses the @migration-task logic for invalid identifiers. Fixes #14277 --- .changeset/mean-worms-fetch.md | 5 +++++ packages/svelte/src/compiler/migrate/index.js | 4 ++-- .../migrate/samples/slot-non-identifier/input.svelte | 8 +++++++- .../migrate/samples/slot-non-identifier/output.svelte | 9 ++++++++- 4 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 .changeset/mean-worms-fetch.md diff --git a/.changeset/mean-worms-fetch.md b/.changeset/mean-worms-fetch.md new file mode 100644 index 0000000000..d573a631de --- /dev/null +++ b/.changeset/mean-worms-fetch.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: bail on named slots with that have reserved keywords during migration diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index 4942dd7db2..9db09158a1 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -17,7 +17,7 @@ import { } from '../utils/ast.js'; import { migrate_svelte_ignore } from '../utils/extract_svelte_ignore.js'; import { validate_component_options } from '../validate-options.js'; -import { is_svg, is_void } from '../../utils.js'; +import { is_reserved, is_svg, is_void } from '../../utils.js'; import { regex_is_valid_identifier } from '../phases/patterns.js'; const regex_style_tags = /(]+>)([\S\s]*?)(<\/style>)/g; @@ -1440,7 +1440,7 @@ function migrate_slot_usage(node, path, state) { if (snippet_name === 'default') { snippet_name = 'children'; } - if (!regex_is_valid_identifier.test(snippet_name)) { + if (!regex_is_valid_identifier.test(snippet_name) || is_reserved(snippet_name)) { has_migration_task = true; state.str.appendLeft( node.start, diff --git a/packages/svelte/tests/migrate/samples/slot-non-identifier/input.svelte b/packages/svelte/tests/migrate/samples/slot-non-identifier/input.svelte index a62eff1b74..7cf7dd86bb 100644 --- a/packages/svelte/tests/migrate/samples/slot-non-identifier/input.svelte +++ b/packages/svelte/tests/migrate/samples/slot-non-identifier/input.svelte @@ -14,6 +14,12 @@ + +
+ reserved keyword +
+
+
cool @@ -63,4 +69,4 @@ cool - \ No newline at end of file + diff --git a/packages/svelte/tests/migrate/samples/slot-non-identifier/output.svelte b/packages/svelte/tests/migrate/samples/slot-non-identifier/output.svelte index 85913264b4..fb262e8c25 100644 --- a/packages/svelte/tests/migrate/samples/slot-non-identifier/output.svelte +++ b/packages/svelte/tests/migrate/samples/slot-non-identifier/output.svelte @@ -16,6 +16,13 @@
+ + +
+ reserved keyword +
+
+ {#snippet stuff()}
@@ -75,4 +82,4 @@ cool - \ No newline at end of file + From f5a7d492162781c09794f207fa9a51c1d4eab51c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 12 Nov 2024 17:59:34 -0500 Subject: [PATCH 152/404] Version Packages (#14274) Co-authored-by: github-actions[bot] --- .changeset/hot-ladybugs-clap.md | 5 ----- .changeset/mean-worms-fetch.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-ladybugs-clap.md delete mode 100644 .changeset/mean-worms-fetch.md diff --git a/.changeset/hot-ladybugs-clap.md b/.changeset/hot-ladybugs-clap.md deleted file mode 100644 index 20acdd0750..0000000000 --- a/.changeset/hot-ladybugs-clap.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: don't wrap pseudo classes inside `:global(...)` with another `:global(...)` during migration diff --git a/.changeset/mean-worms-fetch.md b/.changeset/mean-worms-fetch.md deleted file mode 100644 index d573a631de..0000000000 --- a/.changeset/mean-worms-fetch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: bail on named slots with that have reserved keywords during migration diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index e2c59c27f4..879fdb2058 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.1.16 + +### Patch Changes + +- fix: don't wrap pseudo classes inside `:global(...)` with another `:global(...)` during migration ([#14267](https://github.com/sveltejs/svelte/pull/14267)) + +- fix: bail on named slots with that have reserved keywords during migration ([#14278](https://github.com/sveltejs/svelte/pull/14278)) + ## 5.1.15 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 3124fd73af..760272680e 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.1.15", + "version": "5.1.16", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 20a804671e..c6802cecf0 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.1.15'; +export const VERSION = '5.1.16'; export const PUBLIC_VERSION = '5'; From 25d9aa18286a0fead03f8556eddf71eccf350194 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Wed, 13 Nov 2024 00:18:51 +0000 Subject: [PATCH 153/404] chore: simplify signal capturing logic (#14281) --- .../svelte/src/internal/client/reactivity/props.js | 4 ++-- packages/svelte/src/internal/client/runtime.js | 13 +++++-------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/packages/svelte/src/internal/client/reactivity/props.js b/packages/svelte/src/internal/client/reactivity/props.js index ae934c01a5..dbfb8f0337 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 { active_effect, get, - is_signals_recorded, + captured_signals, set_active_effect, untrack, update @@ -390,7 +390,7 @@ export function prop(props, key, flags, fallback) { return function (/** @type {any} */ value, /** @type {boolean} */ mutation) { // legacy nonsense — need to ensure the source is invalidated when necessary // also needed for when handling inspect logic so we can inspect the correct source signal - if (is_signals_recorded) { + if (captured_signals !== null) { // set this so that we don't reset to the parent value if `d` // is invalidated because of `invalidate_inner_signals` (rather // than because the parent or child value changed) diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index e9f1c773e6..5b5b488824 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -128,8 +128,8 @@ let current_version = 0; // to prevent memory leaks, we skip adding the reaction. export let skip_reaction = false; // Handle collecting all signals which are read during a specific time frame -export let is_signals_recorded = false; -let captured_signals = new Set(); +/** @type {Set | null} */ +export let captured_signals = null; // Handling runtime component context /** @type {ComponentContext | null} */ @@ -732,7 +732,7 @@ export function get(signal) { return value; } - if (is_signals_recorded) { + if (captured_signals !== null) { captured_signals.add(signal); } @@ -800,21 +800,18 @@ export function safe_get(signal) { * @param {() => any} fn */ export function invalidate_inner_signals(fn) { - var previous_is_signals_recorded = is_signals_recorded; var previous_captured_signals = captured_signals; - is_signals_recorded = true; captured_signals = new Set(); var captured = captured_signals; var signal; try { untrack(fn); - } finally { - is_signals_recorded = previous_is_signals_recorded; - if (is_signals_recorded) { + if (previous_captured_signals !== null) { for (signal of captured_signals) { previous_captured_signals.add(signal); } } + } finally { captured_signals = previous_captured_signals; } for (signal of captured) { From ac9b7de05866d2fde6f8fe06281ac97f0e540ec7 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 13 Nov 2024 03:24:56 -0500 Subject: [PATCH 154/404] fix: use strict equality for key block comparisons in runes mode (#14285) fixes #14283 --- .changeset/hot-frogs-melt.md | 5 +++++ .../svelte/src/internal/client/dom/blocks/key.js | 7 +++++-- .../src/internal/client/reactivity/equality.js | 9 +++++++++ .../samples/key-unchanged-value/_config.js | 13 +++++++++++++ .../samples/key-unchanged-value/main.svelte | 10 ++++++++++ 5 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 .changeset/hot-frogs-melt.md create mode 100644 packages/svelte/tests/runtime-runes/samples/key-unchanged-value/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/key-unchanged-value/main.svelte diff --git a/.changeset/hot-frogs-melt.md b/.changeset/hot-frogs-melt.md new file mode 100644 index 0000000000..95b3e0b4b4 --- /dev/null +++ b/.changeset/hot-frogs-melt.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: use strict equality for key block comparisons in runes mode diff --git a/packages/svelte/src/internal/client/dom/blocks/key.js b/packages/svelte/src/internal/client/dom/blocks/key.js index 04b8ab403f..4a8b7b94fc 100644 --- a/packages/svelte/src/internal/client/dom/blocks/key.js +++ b/packages/svelte/src/internal/client/dom/blocks/key.js @@ -1,7 +1,8 @@ /** @import { Effect, TemplateNode } from '#client' */ import { UNINITIALIZED } from '../../../../constants.js'; import { block, branch, pause_effect } from '../../reactivity/effects.js'; -import { safe_not_equal } from '../../reactivity/equality.js'; +import { not_equal, safe_not_equal } from '../../reactivity/equality.js'; +import { is_runes } from '../../runtime.js'; import { hydrate_next, hydrate_node, hydrating } from '../hydration.js'; /** @@ -24,8 +25,10 @@ export function key_block(node, get_key, render_fn) { /** @type {Effect} */ var effect; + var changed = is_runes() ? not_equal : safe_not_equal; + block(() => { - if (safe_not_equal(key, (key = get_key()))) { + if (changed(key, (key = get_key()))) { if (effect) { pause_effect(effect); } diff --git a/packages/svelte/src/internal/client/reactivity/equality.js b/packages/svelte/src/internal/client/reactivity/equality.js index 751c8720f9..37a9994ab8 100644 --- a/packages/svelte/src/internal/client/reactivity/equality.js +++ b/packages/svelte/src/internal/client/reactivity/equality.js @@ -15,6 +15,15 @@ export function safe_not_equal(a, b) { : a !== b || (a !== null && typeof a === 'object') || typeof a === 'function'; } +/** + * @param {unknown} a + * @param {unknown} b + * @returns {boolean} + */ +export function not_equal(a, b) { + return a !== b; +} + /** @type {Equals} */ export function safe_equals(value) { return !safe_not_equal(value, this.v); diff --git a/packages/svelte/tests/runtime-runes/samples/key-unchanged-value/_config.js b/packages/svelte/tests/runtime-runes/samples/key-unchanged-value/_config.js new file mode 100644 index 0000000000..0c56330a3e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/key-unchanged-value/_config.js @@ -0,0 +1,13 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + test({ assert, target, logs }) { + assert.deepEqual(logs, ['rendering']); + + const btn = target.querySelector('button'); + flushSync(() => btn?.click()); + + assert.deepEqual(logs, ['rendering']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/key-unchanged-value/main.svelte b/packages/svelte/tests/runtime-runes/samples/key-unchanged-value/main.svelte new file mode 100644 index 0000000000..47ecd20c10 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/key-unchanged-value/main.svelte @@ -0,0 +1,10 @@ + + + + +{#key outer.inner} + {console.log('rendering')} +{/key} From 45fa678242bec9809b9458a7e0102ceeacc369af Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Thu, 14 Nov 2024 11:24:02 +0100 Subject: [PATCH 155/404] fix: account for `:has(...)` as part of `:root` (#14229) We previously marked all `:root` selectors as global-like, which excempted them from further analysis. This causes problems: - things like `:not(...)` are never visited and therefore never marked as used -> we gotta do that directly when coming across this - `:has(...)` was never visited, too. Just marking it as used is not enough though, because we might need to scope its contents Therefore the logic is enhanced to account for these special cases. Fixes #14118 While fixing this I cleaned up some inconsistencies in what we mark as global. This simplified code and fixed some adjacent bugs, which conindicentally also fixes #14189 --- .changeset/beige-files-pull.md | 5 + .changeset/great-bulldogs-wonder.md | 5 + .../phases/2-analyze/css/css-analyze.js | 47 ++++---- .../phases/2-analyze/css/css-prune.js | 101 +++++++++--------- .../compiler/phases/2-analyze/css/css-warn.js | 7 +- .../compiler/phases/2-analyze/css/utils.js | 84 ++++++++++++++- .../compiler/phases/3-transform/css/index.js | 14 +-- packages/svelte/src/compiler/types/css.d.ts | 4 +- .../svelte/tests/css/samples/has/_config.js | 42 ++++++++ .../svelte/tests/css/samples/has/expected.css | 19 +++- .../svelte/tests/css/samples/has/input.svelte | 15 +++ .../svelte/tests/css/samples/is/_config.js | 40 +++---- .../svelte/tests/css/samples/is/expected.css | 15 +++ .../svelte/tests/css/samples/is/input.svelte | 15 +++ .../samples/not-selector-global/expected.css | 9 ++ .../samples/not-selector-global/input.svelte | 9 ++ .../svelte/tests/css/samples/root/_config.js | 75 ++++++++++++- .../tests/css/samples/root/expected.css | 47 +++++++- .../tests/css/samples/root/expected.html | 2 +- .../tests/css/samples/root/input.svelte | 46 +++++++- 20 files changed, 484 insertions(+), 117 deletions(-) create mode 100644 .changeset/beige-files-pull.md create mode 100644 .changeset/great-bulldogs-wonder.md diff --git a/.changeset/beige-files-pull.md b/.changeset/beige-files-pull.md new file mode 100644 index 0000000000..2cd98a2819 --- /dev/null +++ b/.changeset/beige-files-pull.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: account for `:has(...)` as part of `:root` diff --git a/.changeset/great-bulldogs-wonder.md b/.changeset/great-bulldogs-wonder.md new file mode 100644 index 0000000000..b6cafa4585 --- /dev/null +++ b/.changeset/great-bulldogs-wonder.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: prevent nested pseudo class from being marked as unused 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 ec36e1ce64..38551f328f 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 @@ -4,6 +4,7 @@ import { walk } from 'zimmerframe'; import * as e from '../../../errors.js'; import { is_keyframes_node } from '../../css.js'; +import { is_global, is_unscoped_pseudo_class } from './utils.js'; /** * @typedef {Visitors< @@ -15,27 +16,6 @@ import { is_keyframes_node } from '../../css.js'; * >} CssVisitors */ -/** - * True if is `:global(...)` or `:global` - * @param {Css.RelativeSelector} relative_selector - * @returns {relative_selector is Css.RelativeSelector & { selectors: [Css.PseudoClassSelector, ...Array] }} - */ -function is_global(relative_selector) { - const first = relative_selector.selectors[0]; - - return ( - first.type === 'PseudoClassSelector' && - first.name === 'global' && - (first.args === null || - // Only these two selector types keep the whole selector global, because e.g. - // :global(button).x means that the selector is still scoped because of the .x - relative_selector.selectors.every( - (selector) => - selector.type === 'PseudoClassSelector' || selector.type === 'PseudoElementSelector' - )) - ); -} - /** * True if is `:global` * @param {Css.SimpleSelector} simple_selector @@ -119,11 +99,14 @@ const css_visitors = { node.metadata.rule?.metadata.parent_rule && node.children[0]?.selectors[0]?.type === 'NestingSelector' ) { + const first = node.children[0]?.selectors[1]; + const no_nesting_scope = + first?.type !== 'PseudoClassSelector' || is_unscoped_pseudo_class(first); const parent_is_global = node.metadata.rule.metadata.parent_rule.prelude.children.some( (child) => child.children.length === 1 && child.children[0].metadata.is_global ); // mark `&:hover` in `:global(.foo) { &:hover { color: green }}` as used - if (parent_is_global) { + if (no_nesting_scope && parent_is_global) { node.metadata.used = true; } } @@ -156,9 +139,23 @@ const css_visitors = { ].includes(first.name)); } - node.metadata.is_global_like ||= !!node.selectors.find( - (child) => child.type === 'PseudoClassSelector' && child.name === 'root' - ); + node.metadata.is_global_like ||= + node.selectors.some( + (child) => child.type === 'PseudoClassSelector' && child.name === 'root' + ) && + // :root.y:has(.x) is not a global selector because while .y is unscoped, .x inside `:has(...)` should be scoped + !node.selectors.some((child) => child.type === 'PseudoClassSelector' && child.name === 'has'); + + if (node.metadata.is_global_like || node.metadata.is_global) { + // So that nested selectors like `:root:not(.x)` are not marked as unused + for (const child of node.selectors) { + walk(/** @type {Css.Node} */ (child), null, { + ComplexSelector(node) { + node.metadata.used = true; + } + }); + } + } context.next(); }, 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 a9ea9fe913..bf0fd27566 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,7 +1,7 @@ /** @import { Visitors } from 'zimmerframe' */ /** @import * as Compiler from '#compiler' */ import { walk } from 'zimmerframe'; -import { get_possible_values } from './utils.js'; +import { get_parent_rules, get_possible_values, is_outer_global } 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'; @@ -172,7 +172,7 @@ function get_relative_selectors(node) { } /** - * Discard trailing `:global(...)` selectors without a `:has/is/where/not(...)` modifier, these are unused for scoping purposes + * Discard trailing `:global(...)` selectors, these are unused for scoping purposes * @param {Compiler.Css.ComplexSelector} node */ function truncate(node) { @@ -182,21 +182,22 @@ function truncate(node) { // not after a :global selector !metadata.is_global_like && !(first.type === 'PseudoClassSelector' && first.name === 'global' && first.args === null) && - // not a :global(...) without a :has/is/where/not(...) modifier - (!metadata.is_global || - selectors.some( - (selector) => - selector.type === 'PseudoClassSelector' && - selector.args !== null && - (selector.name === 'has' || - selector.name === 'is' || - selector.name === 'where' || - selector.name === 'not') - )) + // not a :global(...) without a :has/is/where(...) modifier that is scoped + !metadata.is_global ); }); - return node.children.slice(0, i + 1); + return node.children.slice(0, i + 1).map((child) => { + // In case of `:root.y:has(...)`, `y` is unscoped, but everything in `:has(...)` should be scoped (if not global). + // To properly accomplish that, we gotta filter out all selector types except `:has`. + const root = child.selectors.find((s) => s.type === 'PseudoClassSelector' && s.name === 'root'); + if (!root || child.metadata.is_global_like) return child; + + return { + ...child, + selectors: child.selectors.filter((s) => s.type === 'PseudoClassSelector' && s.name === 'has') + }; + }); } /** @@ -334,7 +335,9 @@ function apply_combinator(combinator, relative_selector, parent_selectors, rule, * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement} element */ function mark(relative_selector, element) { - relative_selector.metadata.scoped = true; + if (!is_outer_global(relative_selector)) { + relative_selector.metadata.scoped = true; + } element.metadata.scoped = true; } @@ -415,6 +418,21 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element, /** @type {Array} */ 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). + const rules = [rule, ...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') + ) + ); + if (include_self) { + child_elements.push(element); + descendant_elements.push(element); + } + walk( /** @type {Compiler.SvelteNode} */ (element.fragment), { is_child: true }, @@ -460,7 +478,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element, const descendants = left_most_combinator.name === '+' || left_most_combinator.name === '~' - ? (sibling_elements ??= get_following_sibling_elements(element)) + ? (sibling_elements ??= get_following_sibling_elements(element, include_self)) : left_most_combinator.name === '>' ? child_elements : descendant_elements; @@ -481,20 +499,6 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element, } if (!matched) { - if (relative_selector.metadata.is_global && !relative_selector.metadata.is_global_like) { - // Edge case: `:global(.x):has(.y)` where `.x` is global but `.y` doesn't match. - // Since `used` is set to `true` for `:global(.x)` in css-analyze beforehand, and - // we have no way of knowing if it's safe to set it back to `false`, we'll mark - // the inner selector as used and scoped to prevent it from being pruned, which could - // result in a invalid CSS output (e.g. `.x:has(/* unused .y */)`). The result - // can't match a real element, so the only drawback is the missing prune. - // TODO clean this up some day - complex_selectors[0].metadata.used = true; - complex_selectors[0].children.forEach((selector) => { - selector.metadata.scoped = true; - }); - } - return false; } } @@ -507,9 +511,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element, switch (selector.type) { case 'PseudoClassSelector': { - if (name === 'host' || name === 'root') { - return false; - } + if (name === 'host' || name === 'root') return false; if ( name === 'global' && @@ -578,23 +580,6 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element, } if (!matched) { - if ( - relative_selector.metadata.is_global && - !relative_selector.metadata.is_global_like - ) { - // Edge case: `:global(.x):is(.y)` where `.x` is global but `.y` doesn't match. - // Since `used` is set to `true` for `:global(.x)` in css-analyze beforehand, and - // we have no way of knowing if it's safe to set it back to `false`, we'll mark - // the inner selector as used and scoped to prevent it from being pruned, which could - // result in a invalid CSS output (e.g. `.x:is(/* unused .y */)`). The result - // can't match a real element, so the only drawback is the missing prune. - // TODO clean this up some day - selector.args.children[0].metadata.used = true; - selector.args.children[0].children.forEach((selector) => { - selector.metadata.scoped = true; - }); - } - return false; } } @@ -662,7 +647,10 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element, const parent = /** @type {Compiler.Css.Rule} */ (rule.metadata.parent_rule); for (const complex_selector of parent.prelude.children) { - if (apply_selector(get_relative_selectors(complex_selector), parent, element, state)) { + if ( + apply_selector(get_relative_selectors(complex_selector), parent, element, state) || + complex_selector.children.every((s) => is_global(s, parent)) + ) { complex_selector.metadata.used = true; matched = true; } @@ -681,8 +669,11 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element, return true; } -/** @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement} element */ -function get_following_sibling_elements(element) { +/** + * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement} element + * @param {boolean} include_self + */ +function get_following_sibling_elements(element, include_self) { /** @type {Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.Root | null} */ let parent = get_element_parent(element); @@ -723,6 +714,10 @@ function get_following_sibling_elements(element) { } } + if (include_self) { + sibling_elements.push(element); + } + return sibling_elements; } diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/css-warn.js b/packages/svelte/src/compiler/phases/2-analyze/css/css-warn.js index 482278b795..eab67327e2 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/css-warn.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/css-warn.js @@ -24,7 +24,12 @@ const visitors = { } }, ComplexSelector(node, context) { - if (!node.metadata.used) { + if ( + !node.metadata.used && + // prevent double-marking of `.unused:is(.unused)` + (context.path.at(-2)?.type !== 'PseudoClassSelector' || + /** @type {Css.ComplexSelector} */ (context.path.at(-4))?.metadata.used) + ) { const content = context.state.stylesheet.content; const text = content.styles.substring(node.start - content.start, node.end - content.start); w.css_unused_selector(node, text); 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 99603fad29..0226a150c9 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/utils.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/utils.js @@ -1,4 +1,4 @@ -/** @import { AST } from '#compiler' */ +/** @import { AST, Css } from '#compiler' */ /** @import { Node } from 'estree' */ const UNKNOWN = {}; @@ -33,3 +33,85 @@ export function get_possible_values(chunk) { if (values.has(UNKNOWN)) return null; return values; } + +/** + * Returns all parent rules; root is last + * @param {Css.Rule | null} rule + */ +export function get_parent_rules(rule) { + const parents = []; + + let parent = rule?.metadata.parent_rule; + while (parent) { + parents.push(parent); + parent = parent.metadata.parent_rule; + } + + return parents; +} + +/** + * True if is `:global(...)` or `:global` and no pseudo class that is scoped. + * @param {Css.RelativeSelector} relative_selector + * @returns {relative_selector is Css.RelativeSelector & { selectors: [Css.PseudoClassSelector, ...Array] }} + */ +export function is_global(relative_selector) { + const first = relative_selector.selectors[0]; + + return ( + first.type === 'PseudoClassSelector' && + first.name === 'global' && + (first.args === null || + // Only these two selector types keep the whole selector global, because e.g. + // :global(button).x means that the selector is still scoped because of the .x + relative_selector.selectors.every( + (selector) => + is_unscoped_pseudo_class(selector) || selector.type === 'PseudoElementSelector' + )) + ); +} + +/** + * `true` if is a pseudo class that cannot be or is not scoped + * @param {Css.SimpleSelector} selector + */ +export function is_unscoped_pseudo_class(selector) { + return ( + selector.type === 'PseudoClassSelector' && + // These make the selector scoped + ((selector.name !== 'has' && + selector.name !== 'is' && + selector.name !== 'where' && + // Not is special because we want to scope as specific as possible, but because :not + // inverses the result, we want to leave the unscoped, too. The exception is more than + // one selector in the :not (.e.g :not(.x .y)), then .x and .y should be scoped + (selector.name !== 'not' || + selector.args === null || + selector.args.children.every((c) => c.children.length === 1))) || + // selectors with has/is/where/not can also be global if all their children are global + selector.args === null || + selector.args.children.every((c) => c.children.every((r) => is_global(r)))) + ); +} + +/** + * True if is `:global(...)` or `:global`, irrespective of whether or not there are any pseudo classes that are scoped. + * Difference to `is_global`: `:global(x):has(y)` is `true` for `is_outer_global` but `false` for `is_global`. + * @param {Css.RelativeSelector} relative_selector + * @returns {relative_selector is Css.RelativeSelector & { selectors: [Css.PseudoClassSelector, ...Array] }} + */ +export function is_outer_global(relative_selector) { + const first = relative_selector.selectors[0]; + + return ( + first.type === 'PseudoClassSelector' && + first.name === 'global' && + (first.args === null || + // Only these two selector types can keep the whole selector global, because e.g. + // :global(button).x means that the selector is still scoped because of the .x + relative_selector.selectors.every( + (selector) => + selector.type === 'PseudoClassSelector' || selector.type === 'PseudoElementSelector' + )) + ); +} 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 350eca35d8..d1f3d3baa6 100644 --- a/packages/svelte/src/compiler/phases/3-transform/css/index.js +++ b/packages/svelte/src/compiler/phases/3-transform/css/index.js @@ -292,6 +292,13 @@ const visitors = { context.state.code.prependRight(global.start, '&'); } continue; + } else { + // for any :global() or :global at the middle of compound selector + for (const selector of relative_selector.selectors) { + if (selector.type === 'PseudoClassSelector' && selector.name === 'global') { + remove_global_pseudo_class(selector, null, context.state); + } + } } if (relative_selector.metadata.scoped) { @@ -306,13 +313,6 @@ const visitors = { } } - // for any :global() or :global at the middle of compound selector - for (const selector of relative_selector.selectors) { - if (selector.type === 'PseudoClassSelector' && selector.name === 'global') { - remove_global_pseudo_class(selector, null, context.state); - } - } - if (relative_selector.selectors.some((s) => s.type === 'NestingSelector')) { continue; } diff --git a/packages/svelte/src/compiler/types/css.d.ts b/packages/svelte/src/compiler/types/css.d.ts index 97ac7fc0d3..ba4079ecd0 100644 --- a/packages/svelte/src/compiler/types/css.d.ts +++ b/packages/svelte/src/compiler/types/css.d.ts @@ -87,8 +87,8 @@ export namespace Css { /** * `true` if the whole selector is unscoped, e.g. `:global(...)` or `:global` or `:global.x`. * Selectors like `:global(...).x` are not considered global, because they still need scoping. - * Selectors like `:global(...):is/where/not/has(...)` are considered global even if they aren't - * strictly speaking (we should consolidate the logic around this at some point). + * Selectors like `:global(...):is/where/not/has(...)` are only considered global if all their + * children are global. */ is_global: boolean; /** `:root`, `:host`, `::view-transition`, or selectors after a `:global` */ diff --git a/packages/svelte/tests/css/samples/has/_config.js b/packages/svelte/tests/css/samples/has/_config.js index 8b8e7760f5..e5dc5f3459 100644 --- a/packages/svelte/tests/css/samples/has/_config.js +++ b/packages/svelte/tests/css/samples/has/_config.js @@ -44,6 +44,20 @@ export default test({ character: 401 } }, + { + code: 'css_unused_selector', + message: 'Unused CSS selector ":global(.foo):has(.unused)"', + start: { + line: 40, + column: 1, + character: 422 + }, + end: { + line: 40, + column: 27, + character: 448 + } + }, { code: 'css_unused_selector', message: 'Unused CSS selector "x:has(y):has(.unused)"', @@ -141,6 +155,34 @@ export default test({ column: 11, character: 1336 } + }, + { + code: 'css_unused_selector', + message: 'Unused CSS selector ":has(.unused)"', + start: { + line: 129, + column: 2, + character: 1409 + }, + end: { + line: 129, + column: 15, + character: 1422 + } + }, + { + code: 'css_unused_selector', + message: 'Unused CSS selector "&:has(.unused)"', + start: { + line: 135, + column: 2, + character: 1480 + }, + end: { + line: 135, + column: 16, + character: 1494 + } } ] }); diff --git a/packages/svelte/tests/css/samples/has/expected.css b/packages/svelte/tests/css/samples/has/expected.css index 4cb8d76bf6..8eda676f93 100644 --- a/packages/svelte/tests/css/samples/has/expected.css +++ b/packages/svelte/tests/css/samples/has/expected.css @@ -27,9 +27,9 @@ /* (unused) x:has(.unused) { color: red; }*/ - .foo:has(.unused.svelte-xyz) { + /* (unused) :global(.foo):has(.unused) { color: red; - } + }*/ x.svelte-xyz:has(y:where(.svelte-xyz) /* (unused) .unused*/) { color: green; @@ -111,3 +111,18 @@ /* (unused) x:has(~ y) { color: red; }*/ + + .foo { + .svelte-xyz:has(x:where(.svelte-xyz)) { + color: green; + } + /* (unused) :has(.unused) { + color: red; + }*/ + &:has(x.svelte-xyz) { + color: green; + } + /* (unused) &: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 8eb6296f05..3487b64e8c 100644 --- a/packages/svelte/tests/css/samples/has/input.svelte +++ b/packages/svelte/tests/css/samples/has/input.svelte @@ -121,4 +121,19 @@ x:has(~ y) { color: red; } + + :global(.foo) { + :has(x) { + color: green; + } + :has(.unused) { + color: red; + } + &:has(x) { + color: green; + } + &:has(.unused) { + color: red; + } + } diff --git a/packages/svelte/tests/css/samples/is/_config.js b/packages/svelte/tests/css/samples/is/_config.js index e4c24eb756..eee617e7e4 100644 --- a/packages/svelte/tests/css/samples/is/_config.js +++ b/packages/svelte/tests/css/samples/is/_config.js @@ -30,20 +30,6 @@ export default test({ character: 125 } }, - { - code: 'css_unused_selector', - message: 'Unused CSS selector ".unused"', - start: { - line: 14, - column: 7, - character: 117 - }, - end: { - line: 14, - column: 14, - character: 124 - } - }, { code: 'css_unused_selector', message: 'Unused CSS selector ":global(.foo) :is(.unused)"', @@ -60,16 +46,30 @@ export default test({ }, { code: 'css_unused_selector', - message: 'Unused CSS selector ".unused"', + message: 'Unused CSS selector ":global(.foo):is(.unused)"', start: { - line: 28, - column: 19, - character: 292 + line: 34, + column: 1, + character: 363 }, end: { - line: 28, + line: 34, column: 26, - character: 299 + character: 388 + } + }, + { + code: 'css_unused_selector', + message: 'Unused CSS selector ":is(.unused)"', + start: { + line: 52, + column: 2, + character: 636 + }, + end: { + line: 52, + column: 14, + character: 648 } } ] diff --git a/packages/svelte/tests/css/samples/is/expected.css b/packages/svelte/tests/css/samples/is/expected.css index be8deeff28..639f0d5392 100644 --- a/packages/svelte/tests/css/samples/is/expected.css +++ b/packages/svelte/tests/css/samples/is/expected.css @@ -22,6 +22,12 @@ /* (unused) :global(.foo) :is(.unused) { color: red; }*/ + .foo:is(x.svelte-xyz) { + color: green; + } + /* (unused) :global(.foo):is(.unused) { + color: red; + }*/ x.svelte-xyz :is(html *) { color: green; @@ -32,3 +38,12 @@ y.svelte-xyz :is(x:where(.svelte-xyz) :where(.svelte-xyz)) { color: green; /* matches z */ } + + .foo { + :is(x.svelte-xyz) { + color: green; + } + /* (unused) :is(.unused) { + color: red; + }*/ + } diff --git a/packages/svelte/tests/css/samples/is/input.svelte b/packages/svelte/tests/css/samples/is/input.svelte index 06aa0669b9..b5ae663087 100644 --- a/packages/svelte/tests/css/samples/is/input.svelte +++ b/packages/svelte/tests/css/samples/is/input.svelte @@ -28,6 +28,12 @@ :global(.foo) :is(.unused) { color: red; } + :global(.foo):is(x) { + color: green; + } + :global(.foo):is(.unused) { + color: red; + } x :is(:global(html *)) { color: green; @@ -38,4 +44,13 @@ y :is(x *) { color: green; /* matches z */ } + + :global(.foo) { + :is(x) { + color: green; + } + :is(.unused) { + color: red; + } + } diff --git a/packages/svelte/tests/css/samples/not-selector-global/expected.css b/packages/svelte/tests/css/samples/not-selector-global/expected.css index ea9ff39486..b815dcf9aa 100644 --- a/packages/svelte/tests/css/samples/not-selector-global/expected.css +++ b/packages/svelte/tests/css/samples/not-selector-global/expected.css @@ -27,3 +27,12 @@ span:not(p span) { color: green; } + + .x { + .svelte-xyz:not(.foo) { + color: green; + } + &:not(.foo) { + color: green; + } + } diff --git a/packages/svelte/tests/css/samples/not-selector-global/input.svelte b/packages/svelte/tests/css/samples/not-selector-global/input.svelte index 1f0e6cd6db..1d2f7a9bca 100644 --- a/packages/svelte/tests/css/samples/not-selector-global/input.svelte +++ b/packages/svelte/tests/css/samples/not-selector-global/input.svelte @@ -34,4 +34,13 @@ :global(span:not(p span)) { color: green; } + + :global(.x) { + :not(.foo) { + color: green; + } + &:not(.foo) { + color: green; + } + } \ No newline at end of file diff --git a/packages/svelte/tests/css/samples/root/_config.js b/packages/svelte/tests/css/samples/root/_config.js index f47bee71df..fad7fc536d 100644 --- a/packages/svelte/tests/css/samples/root/_config.js +++ b/packages/svelte/tests/css/samples/root/_config.js @@ -1,3 +1,76 @@ import { test } from '../../test'; -export default test({}); +export default test({ + warnings: [ + { + code: 'css_unused_selector', + message: 'Unused CSS selector ":root .unused"', + start: { + line: 18, + column: 2, + character: 190 + }, + end: { + line: 18, + column: 15, + character: 203 + } + }, + { + code: 'css_unused_selector', + message: 'Unused CSS selector ":root:has(.unused)"', + start: { + line: 25, + column: 2, + character: 269 + }, + end: { + line: 25, + column: 20, + character: 287 + } + }, + { + code: 'css_unused_selector', + message: 'Unused CSS selector ".unused"', + start: { + line: 37, + column: 4, + character: 401 + }, + end: { + line: 37, + column: 11, + character: 408 + } + }, + { + code: 'css_unused_selector', + message: 'Unused CSS selector ":has(.unused)"', + start: { + line: 43, + column: 4, + character: 480 + }, + end: { + line: 43, + column: 17, + character: 493 + } + }, + { + code: 'css_unused_selector', + message: 'Unused CSS selector "&:has(.unused)"', + start: { + line: 49, + column: 4, + character: 566 + }, + end: { + line: 49, + column: 18, + character: 580 + } + } + ] +}); diff --git a/packages/svelte/tests/css/samples/root/expected.css b/packages/svelte/tests/css/samples/root/expected.css index a107696250..b4c82c258e 100644 --- a/packages/svelte/tests/css/samples/root/expected.css +++ b/packages/svelte/tests/css/samples/root/expected.css @@ -1,9 +1,52 @@ + :root { - color: red; + color: green; } .foo:root { - color: blue; + color: green; } :root.foo { color: green; } + :root.unknown { + color: green; + } + + :root h1.svelte-xyz { + color: green; + } + /* (unused) :root .unused { + color: red; + }*/ + + :root:has(h1:where(.svelte-xyz)) { + color: green; + } + /* (unused) :root:has(.unused) { + color: red; + }*/ + + :root:not(.x) { + color: green; + } + + :root { + h1.svelte-xyz { + color: green; + } + /* (unused) .unused { + color: red; + }*/ + .svelte-xyz:has(h1:where(.svelte-xyz)) { + color: green; + } + /* (unused) :has(.unused) { + color: red; + }*/ + &:has(h1.svelte-xyz) { + color: green; + } + /* (unused) &:has(.unused) { + color: red; + }*/ + } diff --git a/packages/svelte/tests/css/samples/root/expected.html b/packages/svelte/tests/css/samples/root/expected.html index 1d90ab5df7..5c30de1c29 100644 --- a/packages/svelte/tests/css/samples/root/expected.html +++ b/packages/svelte/tests/css/samples/root/expected.html @@ -1 +1 @@ -

Hello!

\ No newline at end of file +

Hello!

\ No newline at end of file diff --git a/packages/svelte/tests/css/samples/root/input.svelte b/packages/svelte/tests/css/samples/root/input.svelte index 979c9d4f0a..e06a1a36fa 100644 --- a/packages/svelte/tests/css/samples/root/input.svelte +++ b/packages/svelte/tests/css/samples/root/input.svelte @@ -1,13 +1,55 @@

Hello!

From 320ebd24d8857570b0c180752765fb1580590367 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Thu, 14 Nov 2024 12:54:27 +0100 Subject: [PATCH 156/404] fix: bump `is-reference` dependency to fix `import.meta` bug (#14286) closes #14234 --- .changeset/shaggy-flowers-kneel.md | 5 ++++ packages/svelte/package.json | 2 +- packages/svelte/src/compiler/migrate/index.js | 3 ++ .../src/compiler/phases/2-analyze/index.js | 4 +++ .../visitors/ExportNamedDeclaration.js | 2 ++ .../2-analyze/visitors/ExportSpecifier.js | 15 +++++++--- .../2-analyze/visitors/ImportDeclaration.js | 5 ++-- packages/svelte/src/compiler/utils/ast.js | 11 +++++-- .../svelte/src/compiler/utils/builders.js | 5 ++-- pnpm-lock.yaml | 29 +++++++++++-------- 10 files changed, 57 insertions(+), 24 deletions(-) create mode 100644 .changeset/shaggy-flowers-kneel.md diff --git a/.changeset/shaggy-flowers-kneel.md b/.changeset/shaggy-flowers-kneel.md new file mode 100644 index 0000000000..fb0a041fbc --- /dev/null +++ b/.changeset/shaggy-flowers-kneel.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: bump `is-reference` dependency to fix `import.meta` bug diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 760272680e..608c8d466d 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -144,7 +144,7 @@ "axobject-query": "^4.1.0", "esm-env": "^1.0.0", "esrap": "^1.2.2", - "is-reference": "^3.0.2", + "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index 9db09158a1..88f9bbf0ee 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -507,6 +507,7 @@ const instance_script = { for (let specifier of node.specifiers) { if ( specifier.type === 'ImportSpecifier' && + specifier.imported.type === 'Identifier' && ['beforeUpdate', 'afterUpdate'].includes(specifier.imported.name) ) { const references = state.scope.references.get(specifier.local.name); @@ -544,6 +545,8 @@ const instance_script = { let count_removed = 0; for (const specifier of node.specifiers) { + if (specifier.local.type !== 'Identifier') continue; + const binding = state.scope.get(specifier.local.name); if (binding?.kind === 'bindable_prop') { state.str.remove( diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 9e4f72a66d..a96652d60b 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -474,6 +474,10 @@ export function analyze_component(root, source, options) { } } else { for (const specifier of node.specifiers) { + if (specifier.local.type !== 'Identifier' || specifier.exported.type !== 'Identifier') { + continue; + } + const binding = instance.scope.get(specifier.local.name); if ( 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 a9e8a05c96..784d2fdd88 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportNamedDeclaration.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportNamedDeclaration.js @@ -60,6 +60,8 @@ export function ExportNamedDeclaration(node, context) { if (!context.state.ast_type /* .svelte.js module */ || context.state.ast_type === 'module') { for (const specified of node.specifiers) { + if (specified.local.type !== 'Identifier') continue; + const binding = context.state.scope.get(specified.local.name); if (!binding) continue; 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 27eb39acbf..d0d1ccf932 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportSpecifier.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportSpecifier.js @@ -9,18 +9,25 @@ import * as e from '../../../errors.js'; * @param {Context} context */ export function ExportSpecifier(node, context) { + const local_name = + node.local.type === 'Identifier' ? node.local.name : /** @type {string} */ (node.local.value); + const exported_name = + node.exported.type === 'Identifier' + ? node.exported.name + : /** @type {string} */ (node.exported.value); + if (context.state.ast_type === 'instance') { if (context.state.analysis.runes) { context.state.analysis.exports.push({ - name: node.local.name, - alias: node.exported.name + name: local_name, + alias: exported_name }); - const binding = context.state.scope.get(node.local.name); + const binding = context.state.scope.get(local_name); if (binding) binding.reassigned = binding.updated = true; } } else { - validate_export(node, context.state.scope, node.local.name); + validate_export(node, context.state.scope, local_name); } } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/ImportDeclaration.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/ImportDeclaration.js index eb57fee264..d46218ac64 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/ImportDeclaration.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/ImportDeclaration.js @@ -18,8 +18,9 @@ export function ImportDeclaration(node, context) { for (const specifier of node.specifiers) { if (specifier.type === 'ImportSpecifier') { if ( - specifier.imported.name === 'beforeUpdate' || - specifier.imported.name === 'afterUpdate' + specifier.imported.type === 'Identifier' && + (specifier.imported.name === 'beforeUpdate' || + specifier.imported.name === 'afterUpdate') ) { e.runes_mode_invalid_import(specifier, specifier.imported.name); } diff --git a/packages/svelte/src/compiler/utils/ast.js b/packages/svelte/src/compiler/utils/ast.js index 9aff7546be..cc8339f16b 100644 --- a/packages/svelte/src/compiler/utils/ast.js +++ b/packages/svelte/src/compiler/utils/ast.js @@ -433,7 +433,11 @@ export function is_simple_expression(node) { } if (node.type === 'BinaryExpression' || node.type === 'LogicalExpression') { - return is_simple_expression(node.left) && is_simple_expression(node.right); + return ( + node.left.type !== 'PrivateIdentifier' && + is_simple_expression(node.left) && + is_simple_expression(node.right) + ); } return false; @@ -475,7 +479,10 @@ export function is_expression_async(expression) { case 'AssignmentExpression': case 'BinaryExpression': case 'LogicalExpression': { - return is_expression_async(expression.left) || is_expression_async(expression.right); + return ( + (expression.left.type !== 'PrivateIdentifier' && is_expression_async(expression.left)) || + is_expression_async(expression.right) + ); } case 'CallExpression': case 'NewExpression': { diff --git a/packages/svelte/src/compiler/utils/builders.js b/packages/svelte/src/compiler/utils/builders.js index fc1cdf4adb..ecb595d74d 100644 --- a/packages/svelte/src/compiler/utils/builders.js +++ b/packages/svelte/src/compiler/utils/builders.js @@ -350,7 +350,7 @@ export function prop(kind, key, value, computed = false) { * @returns {ESTree.PropertyDefinition} */ export function prop_def(key, value, computed = false, is_static = false) { - return { type: 'PropertyDefinition', key, value, computed, static: is_static, decorators: [] }; + return { type: 'PropertyDefinition', key, value, computed, static: is_static }; } /** @@ -551,8 +551,7 @@ export function method(kind, key, params, body, computed = false, is_static = fa kind, value: function_builder(null, params, block(body)), computed, - static: is_static, - decorators: [] + static: is_static }; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1f33a207fc..83e4522b2f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -67,7 +67,7 @@ importers: version: 1.5.0 '@types/estree': specifier: ^1.0.5 - version: 1.0.5 + version: 1.0.6 acorn: specifier: ^8.12.1 version: 8.12.1 @@ -87,8 +87,8 @@ importers: specifier: ^1.2.2 version: 1.2.2 is-reference: - specifier: ^3.0.2 - version: 3.0.2 + specifier: ^3.0.3 + version: 3.0.3 locate-character: specifier: ^3.0.0 version: 3.0.0 @@ -916,6 +916,9 @@ packages: '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + '@types/estree@1.0.6': + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -1775,8 +1778,8 @@ packages: is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} - is-reference@3.0.2: - resolution: {integrity: sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==} + is-reference@3.0.3: + resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} is-stream@3.0.0: resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} @@ -3468,7 +3471,7 @@ snapshots: '@rollup/pluginutils@5.1.0(rollup@4.22.4)': dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 estree-walker: 2.0.2 picomatch: 2.3.1 optionalDependencies: @@ -3611,11 +3614,13 @@ snapshots: '@types/eslint@8.56.12': dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 '@types/json-schema': 7.0.15 '@types/estree@1.0.5': {} + '@types/estree@1.0.6': {} + '@types/json-schema@7.0.15': {} '@types/node@12.20.55': {} @@ -4243,7 +4248,7 @@ snapshots: esrap@1.2.2: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 esrecurse@4.3.0: dependencies: @@ -4255,7 +4260,7 @@ snapshots: estree-walker@3.0.3: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 esutils@2.0.3: {} @@ -4556,11 +4561,11 @@ snapshots: is-reference@1.2.1: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 - is-reference@3.0.2: + is-reference@3.0.3: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 is-stream@3.0.0: {} From 6a7146bee7b98d4745b8d13fb5a7d0c3c4720a34 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 12:47:14 -0500 Subject: [PATCH 157/404] Version Packages (#14287) Co-authored-by: github-actions[bot] --- .changeset/beige-files-pull.md | 5 ----- .changeset/great-bulldogs-wonder.md | 5 ----- .changeset/hot-frogs-melt.md | 5 ----- .changeset/shaggy-flowers-kneel.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/beige-files-pull.md delete mode 100644 .changeset/great-bulldogs-wonder.md delete mode 100644 .changeset/hot-frogs-melt.md delete mode 100644 .changeset/shaggy-flowers-kneel.md diff --git a/.changeset/beige-files-pull.md b/.changeset/beige-files-pull.md deleted file mode 100644 index 2cd98a2819..0000000000 --- a/.changeset/beige-files-pull.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: account for `:has(...)` as part of `:root` diff --git a/.changeset/great-bulldogs-wonder.md b/.changeset/great-bulldogs-wonder.md deleted file mode 100644 index b6cafa4585..0000000000 --- a/.changeset/great-bulldogs-wonder.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: prevent nested pseudo class from being marked as unused diff --git a/.changeset/hot-frogs-melt.md b/.changeset/hot-frogs-melt.md deleted file mode 100644 index 95b3e0b4b4..0000000000 --- a/.changeset/hot-frogs-melt.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: use strict equality for key block comparisons in runes mode diff --git a/.changeset/shaggy-flowers-kneel.md b/.changeset/shaggy-flowers-kneel.md deleted file mode 100644 index fb0a041fbc..0000000000 --- a/.changeset/shaggy-flowers-kneel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: bump `is-reference` dependency to fix `import.meta` bug diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 879fdb2058..8040c4ecab 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,17 @@ # svelte +## 5.1.17 + +### Patch Changes + +- fix: account for `:has(...)` as part of `:root` ([#14229](https://github.com/sveltejs/svelte/pull/14229)) + +- fix: prevent nested pseudo class from being marked as unused ([#14229](https://github.com/sveltejs/svelte/pull/14229)) + +- fix: use strict equality for key block comparisons in runes mode ([#14285](https://github.com/sveltejs/svelte/pull/14285)) + +- fix: bump `is-reference` dependency to fix `import.meta` bug ([#14286](https://github.com/sveltejs/svelte/pull/14286)) + ## 5.1.16 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 608c8d466d..ffe19223c2 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.1.16", + "version": "5.1.17", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index c6802cecf0..7c0d0680ea 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.1.16'; +export const VERSION = '5.1.17'; export const PUBLIC_VERSION = '5'; From 8a8e6f70e8955cb3d7aa256710a0ac35ae27f18d Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Thu, 14 Nov 2024 18:48:11 +0100 Subject: [PATCH 158/404] fix: avoid marking subtree as dynamic for inlined attributes (#14269) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: avoid marking subtree as dynamic for inlined attributes * fix: i'm a silly goose 🪿 * chore: refactor `is_inlinable_expression` to accept the attribute * feat: inline dom expression too * fix: special case literals with `"` in it and fix standalone case * chore: simpler check first Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> * typo * add more stuff to snapshot test * simplify/speedup by doing the work once, during analysis * simplify * simplify - no reason these cases should prevent inlining * return template * name is incorrect * name is incorrect * fix escaping * no longer necessary * remove obsolete description * better concatenation * fix test * do the work at runtime * fix another thing * tidy * tidy up * simplify * simplify * fix * note to self * another * simplify * more accurate name * simplify * simplify * explain what is happening * tidy up * simplify * better inlining * update test * colocate some code * better inlining * use attribute metadata * Update packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> * Apply suggestions from code review --------- Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> Co-authored-by: Rich Harris --- .changeset/spotty-sheep-fetch.md | 5 + .../phases/2-analyze/visitors/Attribute.js | 14 +- .../2-analyze/visitors/CallExpression.js | 1 + .../2-analyze/visitors/ExpressionTag.js | 5 - .../phases/2-analyze/visitors/Identifier.js | 22 +++- .../2-analyze/visitors/MemberExpression.js | 1 + .../visitors/TaggedTemplateExpression.js | 1 + .../phases/3-transform/client/utils.js | 52 +------- .../3-transform/client/visitors/Fragment.js | 38 ++++-- .../client/visitors/RegularElement.js | 106 +++++++++------ .../client/visitors/TitleElement.js | 4 +- .../client/visitors/shared/element.js | 4 +- .../client/visitors/shared/fragment.js | 74 ++++++++--- .../client/visitors/shared/utils.js | 124 +++++++++--------- packages/svelte/src/compiler/phases/nodes.js | 3 +- packages/svelte/src/compiler/types/index.d.ts | 2 + packages/svelte/src/internal/client/index.js | 2 + packages/svelte/src/internal/server/index.js | 30 +---- .../svelte/src/internal/shared/attributes.js | 28 ++++ .../main.svelte | 2 +- .../_expected/client/index.svelte.js | 11 +- .../_expected/server/index.svelte.js | 4 +- .../samples/inline-module-vars/index.svelte | 7 +- 23 files changed, 313 insertions(+), 227 deletions(-) create mode 100644 .changeset/spotty-sheep-fetch.md create mode 100644 packages/svelte/src/internal/shared/attributes.js diff --git a/.changeset/spotty-sheep-fetch.md b/.changeset/spotty-sheep-fetch.md new file mode 100644 index 0000000000..8a23f40e3a --- /dev/null +++ b/.changeset/spotty-sheep-fetch.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +feat: better inlining of static attributes 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 2a281a1aa3..6931f873fb 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js @@ -1,7 +1,7 @@ /** @import { ArrowFunctionExpression, Expression, FunctionDeclaration, FunctionExpression } from 'estree' */ /** @import { AST, DelegatedEvent, SvelteNode } from '#compiler' */ /** @import { Context } from '../types' */ -import { is_capture_event, is_delegated } from '../../../../utils.js'; +import { is_boolean_attribute, is_capture_event, is_delegated } from '../../../../utils.js'; import { get_attribute_chunks, get_attribute_expression, @@ -16,14 +16,23 @@ import { mark_subtree_dynamic } from './shared/fragment.js'; export function Attribute(node, context) { context.next(); + const parent = /** @type {SvelteNode} */ (context.path.at(-1)); + // special case if (node.name === 'value') { - const parent = /** @type {SvelteNode} */ (context.path.at(-1)); if (parent.type === 'RegularElement' && parent.name === 'option') { mark_subtree_dynamic(context.path); } } + if (node.name.startsWith('on')) { + mark_subtree_dynamic(context.path); + } + + if (parent.type === 'RegularElement' && is_boolean_attribute(node.name.toLowerCase())) { + node.metadata.expression.can_inline = false; + } + if (node.value !== true) { for (const chunk of get_attribute_chunks(node.value)) { if (chunk.type !== 'ExpressionTag') continue; @@ -37,6 +46,7 @@ export function Attribute(node, context) { node.metadata.expression.has_state ||= chunk.metadata.expression.has_state; node.metadata.expression.has_call ||= chunk.metadata.expression.has_call; + node.metadata.expression.can_inline &&= chunk.metadata.expression.can_inline; } if (is_event_attribute(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 957b27ae9b..2ae32e80e1 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js @@ -178,6 +178,7 @@ export function CallExpression(node, context) { if (!is_pure(node.callee, context) || context.state.expression.dependencies.size > 0) { context.state.expression.has_call = true; context.state.expression.has_state = true; + context.state.expression.can_inline = false; } } } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExpressionTag.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExpressionTag.js index 32c8d2ca36..f59b7fc569 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExpressionTag.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExpressionTag.js @@ -2,7 +2,6 @@ /** @import { Context } from '../types' */ import { is_tag_valid_with_parent } from '../../../../html-tree-validation.js'; import * as e from '../../../errors.js'; -import { mark_subtree_dynamic } from './shared/fragment.js'; /** * @param {AST.ExpressionTag} node @@ -15,9 +14,5 @@ export function ExpressionTag(node, context) { } } - // TODO ideally we wouldn't do this here, we'd just do it on encountering - // an `Identifier` within the tag. But we currently need to handle `{42}` etc - mark_subtree_dynamic(context.path); - context.next({ ...context.state, expression: node.metadata.expression }); } 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..635f939c75 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'; @@ -20,8 +19,6 @@ export function Identifier(node, context) { return; } - mark_subtree_dynamic(context.path); - // If we are using arguments outside of a function, then throw an error if ( node.name === 'arguments' && @@ -87,6 +84,12 @@ export function Identifier(node, context) { } } + // no binding means global, and we can't inline e.g. `{location}` + // because it could change between component renders. if there _is_ a + // binding and it is outside module scope, the expression cannot + // be inlined (TODO allow inlining in more cases - e.g. primitive consts) + let can_inline = !!binding && !binding.scope.parent && binding.kind === 'normal'; + if (binding) { if (context.state.expression) { context.state.expression.dependencies.add(binding); @@ -122,4 +125,17 @@ export function Identifier(node, context) { w.reactive_declaration_module_script_dependency(node); } } + + if (!can_inline && context.state.expression) { + context.state.expression.can_inline = false; + } + + /** + * if the identifier is part of an expression tag of an attribute we want to check if it's inlinable + * before marking the subtree as dynamic. This is because if it's inlinable it will be inlined in the template + * directly making the whole thing actually static. + */ + if (!can_inline || !context.path.find((node) => node.type === 'Attribute')) { + mark_subtree_dynamic(context.path); + } } 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 171a1106a8..adcc2da422 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/MemberExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/MemberExpression.js @@ -19,6 +19,7 @@ export function MemberExpression(node, context) { if (context.state.expression && !is_pure(node, context)) { context.state.expression.has_state = true; + context.state.expression.can_inline = false; } if (!is_safe_identifier(node, context.state.scope)) { 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..724b9af311 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/TaggedTemplateExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/TaggedTemplateExpression.js @@ -10,6 +10,7 @@ export function TaggedTemplateExpression(node, context) { if (context.state.expression && !is_pure(node.tag, context)) { context.state.expression.has_call = true; context.state.expression.has_state = true; + context.state.expression.can_inline = false; } if (node.tag.type === 'Identifier') { 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 c460905977..910f173f79 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/utils.js @@ -1,18 +1,18 @@ /** @import { ArrowFunctionExpression, Expression, FunctionDeclaration, FunctionExpression, Identifier, Pattern, PrivateIdentifier, Statement } from 'estree' */ -/** @import { AST, Binding, SvelteNode } from '#compiler' */ +/** @import { Binding, SvelteNode } 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 { - PROPS_IS_LAZY_INITIAL, + PROPS_IS_BINDABLE, PROPS_IS_IMMUTABLE, + PROPS_IS_LAZY_INITIAL, PROPS_IS_RUNES, - PROPS_IS_UPDATED, - PROPS_IS_BINDABLE + PROPS_IS_UPDATED } from '../../../../constants.js'; import { dev } from '../../../state.js'; +import { extract_identifiers, is_simple_expression } from '../../../utils/ast.js'; +import * as b from '../../../utils/builders.js'; import { get_value } from './visitors/shared/declarations.js'; /** @@ -311,43 +311,3 @@ export function create_derived_block_argument(node, context) { export function create_derived(state, arg) { return b.call(state.analysis.runes ? '$.derived' : '$.derived_safe_equal', arg); } - -/** - * Whether a variable can be referenced directly from template string. - * @param {import('#compiler').Binding | undefined} binding - * @returns {boolean} - */ -export function can_inline_variable(binding) { - return ( - !!binding && - // in a ` - production test + production test + +

{a} + {b} = {a + b}

From 312dd51acc44d92ab855f7eaf15d2d9150c0a6f3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 12:56:16 -0500 Subject: [PATCH 159/404] Version Packages (#14302) Co-authored-by: github-actions[bot] --- .changeset/spotty-sheep-fetch.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/spotty-sheep-fetch.md diff --git a/.changeset/spotty-sheep-fetch.md b/.changeset/spotty-sheep-fetch.md deleted file mode 100644 index 8a23f40e3a..0000000000 --- a/.changeset/spotty-sheep-fetch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': minor ---- - -feat: better inlining of static attributes diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 8040c4ecab..f7e17f14f9 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.2.0 + +### Minor Changes + +- feat: better inlining of static attributes ([#14269](https://github.com/sveltejs/svelte/pull/14269)) + ## 5.1.17 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index ffe19223c2..b23d031a17 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.1.17", + "version": "5.2.0", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 7c0d0680ea..e3d34edc4e 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.1.17'; +export const VERSION = '5.2.0'; export const PUBLIC_VERSION = '5'; From 7fd37740150da964f923f136fed36c2156b533b4 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 14 Nov 2024 20:22:35 +0000 Subject: [PATCH 160/404] fix: disallow invalid attributes for `` and `` (#14228) --- .changeset/fast-ligers-repeat.md | 5 +++++ .../docs/98-reference/.generated/compile-errors.md | 6 ++++++ .../svelte/messages/compile-errors/template.md | 4 ++++ packages/svelte/src/compiler/errors.js | 10 ++++++++++ .../phases/2-analyze/visitors/SvelteDocument.js | 12 ++++++++++++ .../phases/2-analyze/visitors/SvelteWindow.js | 12 ++++++++++++ .../illegal_spread_element-document/errors.json | 14 ++++++++++++++ .../illegal_spread_element-document/input.svelte | 4 ++++ .../illegal_spread_element-window/errors.json | 14 ++++++++++++++ .../illegal_spread_element-window/input.svelte | 4 ++++ 10 files changed, 85 insertions(+) create mode 100644 .changeset/fast-ligers-repeat.md create mode 100644 packages/svelte/tests/validator/samples/illegal_spread_element-document/errors.json create mode 100644 packages/svelte/tests/validator/samples/illegal_spread_element-document/input.svelte create mode 100644 packages/svelte/tests/validator/samples/illegal_spread_element-window/errors.json create mode 100644 packages/svelte/tests/validator/samples/illegal_spread_element-window/input.svelte diff --git a/.changeset/fast-ligers-repeat.md b/.changeset/fast-ligers-repeat.md new file mode 100644 index 0000000000..c6b35c4373 --- /dev/null +++ b/.changeset/fast-ligers-repeat.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: disallow invalid attributes for `` and `` diff --git a/documentation/docs/98-reference/.generated/compile-errors.md b/documentation/docs/98-reference/.generated/compile-errors.md index b32bc6b89c..16cd361e52 100644 --- a/documentation/docs/98-reference/.generated/compile-errors.md +++ b/documentation/docs/98-reference/.generated/compile-errors.md @@ -412,6 +412,12 @@ Expected whitespace `$host()` can only be used inside custom element component instances ``` +### illegal_element_attribute + +``` +`<%name%>` does not support non-event attributes or spread attributes +``` + ### import_svelte_internal_forbidden ``` diff --git a/packages/svelte/messages/compile-errors/template.md b/packages/svelte/messages/compile-errors/template.md index cddca87d9c..38ff87d505 100644 --- a/packages/svelte/messages/compile-errors/template.md +++ b/packages/svelte/messages/compile-errors/template.md @@ -172,6 +172,10 @@ > Expected whitespace +## illegal_element_attribute + +> `<%name%>` does not support non-event attributes or spread attributes + ## js_parse_error > %message% diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js index 00e2246ccf..ad2919b34b 100644 --- a/packages/svelte/src/compiler/errors.js +++ b/packages/svelte/src/compiler/errors.js @@ -984,6 +984,16 @@ export function expected_whitespace(node) { e(node, "expected_whitespace", "Expected whitespace"); } +/** + * `<%name%>` does not support non-event attributes or spread attributes + * @param {null | number | NodeLike} node + * @param {string} name + * @returns {never} + */ +export function illegal_element_attribute(node, name) { + e(node, "illegal_element_attribute", `\`<${name}>\` does not support non-event attributes or spread attributes`); +} + /** * %message% * @param {null | number | NodeLike} node diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteDocument.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteDocument.js index a44baef9e4..fe54ebf30d 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteDocument.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteDocument.js @@ -1,6 +1,8 @@ /** @import { AST } from '#compiler' */ /** @import { Context } from '../types' */ import { disallow_children } from './shared/special-element.js'; +import * as e from '../../../errors.js'; +import { is_event_attribute } from '../../../utils/ast.js'; /** * @param {AST.SvelteDocument} node @@ -8,5 +10,15 @@ import { disallow_children } from './shared/special-element.js'; */ export function SvelteDocument(node, context) { disallow_children(node); + + for (const attribute of node.attributes) { + if ( + attribute.type === 'SpreadAttribute' || + (attribute.type === 'Attribute' && !is_event_attribute(attribute)) + ) { + e.illegal_element_attribute(attribute, 'svelte:document'); + } + } + context.next(); } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteWindow.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteWindow.js index c5392a477d..20f5abc5d6 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteWindow.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteWindow.js @@ -1,6 +1,8 @@ /** @import { AST } from '#compiler' */ /** @import { Context } from '../types' */ import { disallow_children } from './shared/special-element.js'; +import * as e from '../../../errors.js'; +import { is_event_attribute } from '../../../utils/ast.js'; /** * @param {AST.SvelteWindow} node @@ -8,5 +10,15 @@ import { disallow_children } from './shared/special-element.js'; */ export function SvelteWindow(node, context) { disallow_children(node); + + for (const attribute of node.attributes) { + if ( + attribute.type === 'SpreadAttribute' || + (attribute.type === 'Attribute' && !is_event_attribute(attribute)) + ) { + e.illegal_element_attribute(attribute, 'svelte:window'); + } + } + context.next(); } diff --git a/packages/svelte/tests/validator/samples/illegal_spread_element-document/errors.json b/packages/svelte/tests/validator/samples/illegal_spread_element-document/errors.json new file mode 100644 index 0000000000..2e15b152e7 --- /dev/null +++ b/packages/svelte/tests/validator/samples/illegal_spread_element-document/errors.json @@ -0,0 +1,14 @@ +[ + { + "code": "illegal_element_attribute", + "message": "`` does not support non-event attributes or spread attributes", + "start": { + "line": 4, + "column": 17 + }, + "end": { + "line": 4, + "column": 23 + } + } +] diff --git a/packages/svelte/tests/validator/samples/illegal_spread_element-document/input.svelte b/packages/svelte/tests/validator/samples/illegal_spread_element-document/input.svelte new file mode 100644 index 0000000000..8980eea012 --- /dev/null +++ b/packages/svelte/tests/validator/samples/illegal_spread_element-document/input.svelte @@ -0,0 +1,4 @@ + + diff --git a/packages/svelte/tests/validator/samples/illegal_spread_element-window/errors.json b/packages/svelte/tests/validator/samples/illegal_spread_element-window/errors.json new file mode 100644 index 0000000000..c59045e852 --- /dev/null +++ b/packages/svelte/tests/validator/samples/illegal_spread_element-window/errors.json @@ -0,0 +1,14 @@ +[ + { + "code": "illegal_element_attribute", + "message": "`` does not support non-event attributes or spread attributes", + "start": { + "line": 4, + "column": 15 + }, + "end": { + "line": 4, + "column": 21 + } + } +] diff --git a/packages/svelte/tests/validator/samples/illegal_spread_element-window/input.svelte b/packages/svelte/tests/validator/samples/illegal_spread_element-window/input.svelte new file mode 100644 index 0000000000..c0076cc6bf --- /dev/null +++ b/packages/svelte/tests/validator/samples/illegal_spread_element-window/input.svelte @@ -0,0 +1,4 @@ + + From e379319626349e55b65a7f6af18eef4a81f92adf Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Thu, 14 Nov 2024 22:47:18 +0100 Subject: [PATCH 161/404] fix: ensure props passed to components via mount are updateable (#14210) The detection whether or not props are writable is buggy; it doesn't take into account the case when instantiating components via `mount` or legacy-`new` Fixes #14161 This posed an interesting question: What to do about (non-)`bind:`able properties? The answer I arrived on was: Treat it as if everything that _can_ be bound _is_ treated as bound, and everything else as readonly. In other words, if you're reassigning a prop, it will diverge from the passed in props if it's not bindable or not set in the parent, otherwise it will mutate the passed in props. I think that makes the most sense, given that you can't control this behavior from the outside. --- .changeset/fresh-pigs-divide.md | 5 ++ .../svelte/src/internal/client/constants.js | 1 + .../src/internal/client/reactivity/props.js | 20 +++++++- .../svelte/src/internal/client/validate.js | 11 +---- packages/svelte/src/legacy/legacy-client.js | 7 ++- .../samples/mount-props-updates/_config.js | 47 +++++++++++++++++++ .../mount-props-updates/component.svelte | 17 +++++++ .../samples/mount-props-updates/main.svelte | 30 ++++++++++++ 8 files changed, 124 insertions(+), 14 deletions(-) create mode 100644 .changeset/fresh-pigs-divide.md create mode 100644 packages/svelte/tests/runtime-runes/samples/mount-props-updates/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/mount-props-updates/component.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/mount-props-updates/main.svelte diff --git a/.changeset/fresh-pigs-divide.md b/.changeset/fresh-pigs-divide.md new file mode 100644 index 0000000000..8aa3d74ce9 --- /dev/null +++ b/.changeset/fresh-pigs-divide.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure props passed to components via mount are updateable diff --git a/packages/svelte/src/internal/client/constants.js b/packages/svelte/src/internal/client/constants.js index 53df86126a..bcf30e78db 100644 --- a/packages/svelte/src/internal/client/constants.js +++ b/packages/svelte/src/internal/client/constants.js @@ -22,4 +22,5 @@ export const EFFECT_HAS_DERIVED = 1 << 19; 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/reactivity/props.js b/packages/svelte/src/internal/client/reactivity/props.js index dbfb8f0337..8fb13f7e2c 100644 --- a/packages/svelte/src/internal/client/reactivity/props.js +++ b/packages/svelte/src/internal/client/reactivity/props.js @@ -20,7 +20,13 @@ import { } from '../runtime.js'; import { safe_equals } from './equality.js'; import * as e from '../errors.js'; -import { BRANCH_EFFECT, LEGACY_DERIVED_PROP, ROOT_EFFECT } from '../constants.js'; +import { + BRANCH_EFFECT, + LEGACY_DERIVED_PROP, + LEGACY_PROPS, + ROOT_EFFECT, + 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'; @@ -209,6 +215,9 @@ const spread_props_handler = { } }, has(target, key) { + // To prevent a false positive `is_entry_props` in the `prop` function + if (key === STATE_SYMBOL || key === LEGACY_PROPS) return false; + for (let p of target.props) { if (is_function(p)) p = p(); if (p != null && key in p) return true; @@ -282,7 +291,14 @@ export function prop(props, key, flags, fallback) { } else { prop_value = /** @type {V} */ (props[key]); } - var setter = get_descriptor(props, key)?.set; + + // Can be the case when someone does `mount(Component, props)` with `let props = $state({...})` + // or `createClassComponent(Component, props)` + 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); 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 eddcb69d9e..951feee33b 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, untrack } from './runtime.js'; +import { dev_current_component_function } from './runtime.js'; import { get_descriptor, is_array } from '../shared/utils.js'; import * as e from './errors.js'; import { FILENAME } from '../../constants.js'; @@ -6,15 +6,6 @@ import { render_effect } from './reactivity/effects.js'; import * as w from './warnings.js'; import { capture_store_binding } from './reactivity/store.js'; -/** regex of all html void element names */ -const void_element_names = - /^(?:area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/; - -/** @param {string} tag */ -function is_void(tag) { - return void_element_names.test(tag) || tag.toLowerCase() === '!doctype'; -} - /** * @param {() => any} collection * @param {(item: any, index: number) => string} key_fn diff --git a/packages/svelte/src/legacy/legacy-client.js b/packages/svelte/src/legacy/legacy-client.js index c7684570bb..9e1cd888ad 100644 --- a/packages/svelte/src/legacy/legacy-client.js +++ b/packages/svelte/src/legacy/legacy-client.js @@ -1,5 +1,5 @@ /** @import { ComponentConstructorOptions, ComponentType, SvelteComponent, Component } from 'svelte' */ -import { DIRTY, MAYBE_DIRTY } from '../internal/client/constants.js'; +import { DIRTY, LEGACY_PROPS, MAYBE_DIRTY } from '../internal/client/constants.js'; 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'; @@ -89,7 +89,7 @@ class Svelte4Component { }; // Replicate coarse-grained props through a proxy that has a version source for - // each property, which is increment on updates to the property itself. Do not + // each property, which is incremented on updates to the property itself. Do not // use our $state proxy because that one has fine-grained reactivity. const props = new Proxy( { ...(options.props || {}), $$events: {} }, @@ -98,6 +98,9 @@ class Svelte4Component { return get(sources.get(prop) ?? add_source(prop, Reflect.get(target, prop))); }, has(target, prop) { + // Necessary to not throw "invalid binding" validation errors on the component side + if (prop === LEGACY_PROPS) return true; + get(sources.get(prop) ?? add_source(prop, Reflect.get(target, prop))); return Reflect.has(target, prop); }, diff --git a/packages/svelte/tests/runtime-runes/samples/mount-props-updates/_config.js b/packages/svelte/tests/runtime-runes/samples/mount-props-updates/_config.js new file mode 100644 index 0000000000..ff7af2d524 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/mount-props-updates/_config.js @@ -0,0 +1,47 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + test({ assert, target }) { + assert.htmlEqual( + target.innerHTML, + // The buz fallback does not propagate back up + ` + foo baz +
foo bar baz buz
+
foo bar baz buz
+ ` + ); + + const [btn1, btn2, btn3] = target.querySelectorAll('button'); + + btn2.click(); + btn3.click(); + flushSync(); + assert.htmlEqual( + target.innerHTML, + // bar is not set in the parent because it's a readonly property + // baz is not set in the parent because while it's a bindable property, + // it wasn't set initially so it's treated as a readonly proeprty + ` + foo 3 +
1 2 3 4
+
1 2 3 4
+ ` + ); + + btn1.click(); + flushSync(); + assert.htmlEqual( + target.innerHTML, + // Because foo is a readonly property, component.svelte diverges locally from it, + // and the passed in property keeps the initial value of foo. This is why it stays + // at 1, because foo is not updated to a different value. + ` + foo bar baz buz +
1 bar baz buz
+
1 bar baz buz
+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/mount-props-updates/component.svelte b/packages/svelte/tests/runtime-runes/samples/mount-props-updates/component.svelte new file mode 100644 index 0000000000..214cb7bf72 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/mount-props-updates/component.svelte @@ -0,0 +1,17 @@ + + + + +{foo} +{bar} +{baz} +{buz} diff --git a/packages/svelte/tests/runtime-runes/samples/mount-props-updates/main.svelte b/packages/svelte/tests/runtime-runes/samples/mount-props-updates/main.svelte new file mode 100644 index 0000000000..0769830afb --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/mount-props-updates/main.svelte @@ -0,0 +1,30 @@ + + + {props.foo} {props.bar} {props.baz} {props.buz} +
+
From 637364104538cdac26e3807fd71e4be6dd2958f8 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:32:47 +0100 Subject: [PATCH 162/404] docs: add example to "state instead of stores" (#14310) closes #13879 --- documentation/docs/06-runtime/01-stores.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/documentation/docs/06-runtime/01-stores.md b/documentation/docs/06-runtime/01-stores.md index 8ea01e5314..9cff5a754f 100644 --- a/documentation/docs/06-runtime/01-stores.md +++ b/documentation/docs/06-runtime/01-stores.md @@ -38,6 +38,28 @@ Prior to Svelte 5, stores were the go-to solution for creating cross-component r - when extracting logic, it's better to take advantage of runes' universal reactivity: You can use runes outside the top level of components and even place them into JavaScript or TypeScript files (using a `.svelte.js` or `.svelte.ts` file ending) - when creating shared state, you can create a `$state` object containing the values you need and then manipulate said state +```ts +/// file: state.svelte.js +export const userState = $state({ + name: 'name', + /* ... */ +}); +``` + +```svelte + + + +

User name: {userState.name}

+ +``` + Stores are still a good solution when you have complex asynchronous data streams or it's important to have more manual control over updating values or listening to changes. If you're familiar with RxJs and want to reuse that knowledge, the `$` also comes in handy for you. ## svelte/store From efc65d4e0c185cd597a6eeef013f6641b8e25d50 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:33:10 +0100 Subject: [PATCH 163/404] fix: mark pseudo classes nested inside `:not` as used (#14303) fixes the css bug part of #14299 --- .changeset/chatty-singers-sin.md | 5 +++++ .../src/compiler/phases/2-analyze/css/css-analyze.js | 3 ++- .../svelte/src/compiler/phases/2-analyze/css/css-prune.js | 7 ++++++- .../svelte/tests/css/samples/not-selector/expected.css | 4 ++++ .../svelte/tests/css/samples/not-selector/input.svelte | 4 ++++ 5 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 .changeset/chatty-singers-sin.md diff --git a/.changeset/chatty-singers-sin.md b/.changeset/chatty-singers-sin.md new file mode 100644 index 0000000000..f69bc97454 --- /dev/null +++ b/.changeset/chatty-singers-sin.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: mark pseudo classes nested inside `:not` as used 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 38551f328f..1dd2d9ae7c 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 @@ -150,8 +150,9 @@ const css_visitors = { // So that nested selectors like `:root:not(.x)` are not marked as unused for (const child of node.selectors) { walk(/** @type {Css.Node} */ (child), null, { - ComplexSelector(node) { + ComplexSelector(node, context) { node.metadata.used = true; + context.next(); } }); } 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 bf0fd27566..bfb83d3e02 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 @@ -531,7 +531,12 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element, // with descendants, in which case we scope them all. if (name === 'not' && selector.args) { for (const complex_selector of selector.args.children) { - complex_selector.metadata.used = true; + walk(complex_selector, null, { + ComplexSelector(node, context) { + node.metadata.used = true; + context.next(); + } + }); const relative = truncate(complex_selector); if (complex_selector.children.length > 1) { diff --git a/packages/svelte/tests/css/samples/not-selector/expected.css b/packages/svelte/tests/css/samples/not-selector/expected.css index ea0b3aacac..1b0db0ff4b 100644 --- a/packages/svelte/tests/css/samples/not-selector/expected.css +++ b/packages/svelte/tests/css/samples/not-selector/expected.css @@ -29,3 +29,7 @@ span.svelte-xyz:not(:focus) { color: green; } + + p.svelte-xyz:not(:has(span)) { + color: green; + } diff --git a/packages/svelte/tests/css/samples/not-selector/input.svelte b/packages/svelte/tests/css/samples/not-selector/input.svelte index 3a786530a2..da8f4579c2 100644 --- a/packages/svelte/tests/css/samples/not-selector/input.svelte +++ b/packages/svelte/tests/css/samples/not-selector/input.svelte @@ -36,4 +36,8 @@ span:not(:focus) { color: green; } + + p:not(:has(span)) { + color: green; + } \ No newline at end of file From 1f0700f5c597174e51d6e07407b078c8314486fc Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Fri, 15 Nov 2024 23:40:08 +0100 Subject: [PATCH 164/404] fix: mark subtree dynamic for img with loading attribute (#14317) * fix: mark subtree dynamic for img with loading attribute * chore: unify conditions * chore: change conditions --- .changeset/rotten-turkeys-sniff.md | 5 +++++ .../compiler/phases/2-analyze/visitors/Attribute.js | 11 ++++++++--- .../_expected/client/index.svelte.js | 6 +++++- .../_expected/server/index.svelte.js | 2 +- .../snapshot/samples/skip-static-subtree/index.svelte | 5 ++++- 5 files changed, 23 insertions(+), 6 deletions(-) create mode 100644 .changeset/rotten-turkeys-sniff.md diff --git a/.changeset/rotten-turkeys-sniff.md b/.changeset/rotten-turkeys-sniff.md new file mode 100644 index 0000000000..53fb5bd663 --- /dev/null +++ b/.changeset/rotten-turkeys-sniff.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: mark subtree dynamic for img with loading 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 6931f873fb..ca0ea55d1f 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js @@ -18,9 +18,14 @@ export function Attribute(node, context) { const parent = /** @type {SvelteNode} */ (context.path.at(-1)); - // special case - if (node.name === 'value') { - if (parent.type === 'RegularElement' && parent.name === 'option') { + if (parent.type === 'RegularElement') { + // special case - \ No newline at end of file + +
+ +
\ No newline at end of file From 94471ca38e9752c08dde1620916b76ff517b369b Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Fri, 15 Nov 2024 23:50:47 +0100 Subject: [PATCH 165/404] fix: avoid relying on Node specifics within compiler (#14314) fixes #14294 --- .changeset/strange-adults-visit.md | 5 +++++ packages/svelte/src/compiler/validate-options.js | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 .changeset/strange-adults-visit.md diff --git a/.changeset/strange-adults-visit.md b/.changeset/strange-adults-visit.md new file mode 100644 index 0000000000..06cc667dac --- /dev/null +++ b/.changeset/strange-adults-visit.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: avoid relying on Node specifics within compiler diff --git a/packages/svelte/src/compiler/validate-options.js b/packages/svelte/src/compiler/validate-options.js index 9465cba77a..ab932ed5bc 100644 --- a/packages/svelte/src/compiler/validate-options.js +++ b/packages/svelte/src/compiler/validate-options.js @@ -1,5 +1,3 @@ -import process from 'node:process'; - /** @import { ModuleCompileOptions, ValidatedModuleCompileOptions, CompileOptions, ValidatedCompileOptions } from '#compiler' */ import * as e from './errors.js'; import * as w from './warnings.js'; @@ -13,9 +11,19 @@ import * as w from './warnings.js'; const common = { filename: string('(unknown)'), - // default to process.cwd() where it exists to replicate svelte4 behavior + // default to process.cwd() where it exists to replicate svelte4 behavior (and make Deno work with this as well) // see https://github.com/sveltejs/svelte/blob/b62fc8c8fd2640c9b99168f01b9d958cb2f7574f/packages/svelte/src/compiler/compile/Component.js#L211 - rootDir: string(typeof process !== 'undefined' ? process.cwd?.() : undefined), + /* eslint-disable */ + rootDir: string( + typeof process !== 'undefined' + ? process.cwd?.() + : // @ts-expect-error + typeof Deno !== 'undefined' + ? // @ts-expect-error + Deno.cwd() + : undefined + ), + /* eslint-enable */ dev: boolean(false), From 95ab85fad742d9911bbe87561dfaf9f868f75eaf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 16 Nov 2024 00:07:08 +0000 Subject: [PATCH 166/404] Version Packages (#14304) Co-authored-by: github-actions[bot] --- .changeset/chatty-singers-sin.md | 5 ----- .changeset/fast-ligers-repeat.md | 5 ----- .changeset/fresh-pigs-divide.md | 5 ----- .changeset/rotten-turkeys-sniff.md | 5 ----- .changeset/strange-adults-visit.md | 5 ----- packages/svelte/CHANGELOG.md | 14 ++++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 8 files changed, 16 insertions(+), 27 deletions(-) delete mode 100644 .changeset/chatty-singers-sin.md delete mode 100644 .changeset/fast-ligers-repeat.md delete mode 100644 .changeset/fresh-pigs-divide.md delete mode 100644 .changeset/rotten-turkeys-sniff.md delete mode 100644 .changeset/strange-adults-visit.md diff --git a/.changeset/chatty-singers-sin.md b/.changeset/chatty-singers-sin.md deleted file mode 100644 index f69bc97454..0000000000 --- a/.changeset/chatty-singers-sin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: mark pseudo classes nested inside `:not` as used diff --git a/.changeset/fast-ligers-repeat.md b/.changeset/fast-ligers-repeat.md deleted file mode 100644 index c6b35c4373..0000000000 --- a/.changeset/fast-ligers-repeat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: disallow invalid attributes for `` and `` diff --git a/.changeset/fresh-pigs-divide.md b/.changeset/fresh-pigs-divide.md deleted file mode 100644 index 8aa3d74ce9..0000000000 --- a/.changeset/fresh-pigs-divide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure props passed to components via mount are updateable diff --git a/.changeset/rotten-turkeys-sniff.md b/.changeset/rotten-turkeys-sniff.md deleted file mode 100644 index 53fb5bd663..0000000000 --- a/.changeset/rotten-turkeys-sniff.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: mark subtree dynamic for img with loading attribute diff --git a/.changeset/strange-adults-visit.md b/.changeset/strange-adults-visit.md deleted file mode 100644 index 06cc667dac..0000000000 --- a/.changeset/strange-adults-visit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: avoid relying on Node specifics within compiler diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index f7e17f14f9..5888348409 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,19 @@ # svelte +## 5.2.1 + +### Patch Changes + +- fix: mark pseudo classes nested inside `:not` as used ([#14303](https://github.com/sveltejs/svelte/pull/14303)) + +- fix: disallow invalid attributes for `` and `` ([#14228](https://github.com/sveltejs/svelte/pull/14228)) + +- fix: ensure props passed to components via mount are updateable ([#14210](https://github.com/sveltejs/svelte/pull/14210)) + +- fix: mark subtree dynamic for img with loading attribute ([#14317](https://github.com/sveltejs/svelte/pull/14317)) + +- fix: avoid relying on Node specifics within compiler ([#14314](https://github.com/sveltejs/svelte/pull/14314)) + ## 5.2.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index b23d031a17..9989394873 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.2.0", + "version": "5.2.1", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index e3d34edc4e..1f6874e75e 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.2.0'; +export const VERSION = '5.2.1'; export const PUBLIC_VERSION = '5'; From 3a69b4c41533757ea0e3c7e293bd41818a833ead Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Sat, 16 Nov 2024 17:10:45 +0000 Subject: [PATCH 167/404] fix: ensure inline object literals are correctly serialised (#14325) * fix: ensure inline object literals are correctly serialised * Apply suggestions from code review * address feedback * address feedback --------- Co-authored-by: Rich Harris --- .changeset/wicked-readers-knock.md | 5 ++++ .../phases/2-analyze/visitors/shared/utils.js | 27 ++++++++++++++++--- .../samples/inline-expressions-2/_config.js | 11 ++++++++ .../samples/inline-expressions-2/main.svelte | 6 +++++ 4 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 .changeset/wicked-readers-knock.md create mode 100644 packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/main.svelte diff --git a/.changeset/wicked-readers-knock.md b/.changeset/wicked-readers-knock.md new file mode 100644 index 0000000000..4ec417402f --- /dev/null +++ b/.changeset/wicked-readers-knock.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: treat property accesses of literals as pure 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 82356ea619..2dec4361c8 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, Pattern, PrivateIdentifier, Super, UpdateExpression, VariableDeclarator } from 'estree' */ +/** @import { AssignmentExpression, Expression, Literal, Pattern, PrivateIdentifier, Super, UpdateExpression, VariableDeclarator } from 'estree' */ /** @import { AST, Binding } from '#compiler' */ /** @import { AnalysisState, Context } from '../../types' */ /** @import { Scope } from '../../../scope' */ @@ -176,21 +176,42 @@ export function is_safe_identifier(expression, scope) { } /** - * @param {Expression | Super} node + * @param {Expression | Literal | Super} node * @param {Context} context * @returns {boolean} */ export function is_pure(node, context) { + if (node.type === 'Literal') { + return true; + } + if (node.type === 'CallExpression') { + if (!is_pure(node.callee, context)) { + return false; + } + for (let arg of node.arguments) { + if (!is_pure(arg.type === 'SpreadElement' ? arg.argument : arg, context)) { + return false; + } + } + return true; + } if (node.type !== 'Identifier' && node.type !== 'MemberExpression') { return false; } - const left = object(node); + /** @type {Expression | Super | null} */ + let left = node; + while (left.type === 'MemberExpression') { + left = left.object; + } + if (!left) return false; if (left.type === 'Identifier') { const binding = context.state.scope.get(left.name); if (binding === null) return true; // globals are assumed to be safe + } else if (is_pure(left, context)) { + return true; } // TODO add more cases (safe Svelte imports, etc) diff --git a/packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/_config.js b/packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/_config.js new file mode 100644 index 0000000000..1869540b2c --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/_config.js @@ -0,0 +1,11 @@ +import { test } from '../../test'; + +export default test({ + html: ` +

Without text expression: 7.36

+

With text expression: 7.36

+

With text expression and function call: 7.36

+

With text expression and property access: 4

+

Hello name!

+

4

` +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/main.svelte b/packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/main.svelte new file mode 100644 index 0000000000..78a325dcc4 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/main.svelte @@ -0,0 +1,6 @@ +

Without text expression: 7.36

+

With text expression: {7.36}

+

With text expression and function call: {(7.36).toLocaleString()}

+

With text expression and property access: {"test".length}

+

Hello {('name').toUpperCase().toLowerCase()}!

+

{"test".length}

From 396ea2ef370e7ea5b5d4571c4e5e14384bac3ca6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 16 Nov 2024 17:13:21 +0000 Subject: [PATCH 168/404] Version Packages (#14328) Co-authored-by: github-actions[bot] --- .changeset/wicked-readers-knock.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/wicked-readers-knock.md diff --git a/.changeset/wicked-readers-knock.md b/.changeset/wicked-readers-knock.md deleted file mode 100644 index 4ec417402f..0000000000 --- a/.changeset/wicked-readers-knock.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: treat property accesses of literals as pure diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 5888348409..64b04e0fc2 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.2.2 + +### Patch Changes + +- fix: treat property accesses of literals as pure ([#14325](https://github.com/sveltejs/svelte/pull/14325)) + ## 5.2.1 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 9989394873..8c9efb00de 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.2.1", + "version": "5.2.2", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 1f6874e75e..db950a129a 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.2.1'; +export const VERSION = '5.2.2'; export const PUBLIC_VERSION = '5'; From 24ebbcbbc19701987d7560c4fe3952e8c24432ef Mon Sep 17 00:00:00 2001 From: Gwen Le Bihan Date: Sun, 17 Nov 2024 23:27:34 +0100 Subject: [PATCH 169/404] docs: use e.detail instead of e.details in migration examples (#14340) --- documentation/docs/07-misc/07-v5-migration-guide.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/docs/07-misc/07-v5-migration-guide.md b/documentation/docs/07-misc/07-v5-migration-guide.md index 4ebfb5f905..687f8e93b1 100644 --- a/documentation/docs/07-misc/07-v5-migration-guide.md +++ b/documentation/docs/07-misc/07-v5-migration-guide.md @@ -169,11 +169,11 @@ This function is deprecated in Svelte 5. Instead, components should accept _call { - size += power---.details---; + size += power---.detail---; if (size > 75) burst = true; }} ---on:---deflate={(power) => { - if (size > 0) size -= power---.details---; + if (size > 0) size -= power---.detail---; }} /> @@ -317,7 +317,7 @@ When spreading props, local event handlers must go _after_ the spread, or they r > - import the function > - call the function to get a dispatch function > - call said dispatch function with a string and possibly a payload -> - retrieve said payload on the other end through a `.details` property, because the event itself was always a `CustomEvent` +> - retrieve said payload on the other end through a `.detail` property, because the event itself was always a `CustomEvent` > > It was always possible to use component callback props, but because you had to listen to DOM events using `on:`, it made sense to use `createEventDispatcher` for component events due to syntactical consistency. Now that we have event attributes (`onclick`), it's the other way around: Callback props are now the more sensible thing to do. > From 1c454c236eeb00fab04007b4c8b9ae0f475e9cf6 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Mon, 18 Nov 2024 14:24:55 +0000 Subject: [PATCH 170/404] fix: ensure dynamic call expressions correctly generate output (#14345) --- .changeset/moody-bears-change.md | 5 +++++ .../src/compiler/phases/2-analyze/visitors/CallExpression.js | 2 ++ .../samples/inline-expressions-subtree/_config.js | 5 +++++ .../samples/inline-expressions-subtree/main.svelte | 4 ++++ 4 files changed, 16 insertions(+) create mode 100644 .changeset/moody-bears-change.md create mode 100644 packages/svelte/tests/runtime-legacy/samples/inline-expressions-subtree/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/inline-expressions-subtree/main.svelte diff --git a/.changeset/moody-bears-change.md b/.changeset/moody-bears-change.md new file mode 100644 index 0000000000..0bf32026d2 --- /dev/null +++ b/.changeset/moody-bears-change.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure dynamic call expressions correctly generate output 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 2ae32e80e1..fe3f638a69 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js @@ -5,6 +5,7 @@ import { get_rune } from '../../scope.js'; import * as e from '../../../errors.js'; import { get_parent, unwrap_optional } from '../../../utils/ast.js'; import { is_pure, is_safe_identifier } from './shared/utils.js'; +import { mark_subtree_dynamic } from './shared/fragment.js'; /** * @param {CallExpression} node @@ -179,6 +180,7 @@ export function CallExpression(node, context) { context.state.expression.has_call = true; context.state.expression.has_state = true; context.state.expression.can_inline = false; + mark_subtree_dynamic(context.path); } } } diff --git a/packages/svelte/tests/runtime-legacy/samples/inline-expressions-subtree/_config.js b/packages/svelte/tests/runtime-legacy/samples/inline-expressions-subtree/_config.js new file mode 100644 index 0000000000..17c573a510 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/inline-expressions-subtree/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + html: 'A\n
B
' +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/inline-expressions-subtree/main.svelte b/packages/svelte/tests/runtime-legacy/samples/inline-expressions-subtree/main.svelte new file mode 100644 index 0000000000..555dc8af23 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/inline-expressions-subtree/main.svelte @@ -0,0 +1,4 @@ + 'red')()}>A +
+ 'red')()}>B +
From b145035a00525770abe9116e3887c050e19340ea Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 15:23:13 +0000 Subject: [PATCH 171/404] Version Packages (#14348) Co-authored-by: github-actions[bot] --- .changeset/moody-bears-change.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/moody-bears-change.md diff --git a/.changeset/moody-bears-change.md b/.changeset/moody-bears-change.md deleted file mode 100644 index 0bf32026d2..0000000000 --- a/.changeset/moody-bears-change.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure dynamic call expressions correctly generate output diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 64b04e0fc2..d36307c80c 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.2.3 + +### Patch Changes + +- fix: ensure dynamic call expressions correctly generate output ([#14345](https://github.com/sveltejs/svelte/pull/14345)) + ## 5.2.2 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 8c9efb00de..50319c77aa 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.2.2", + "version": "5.2.3", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index db950a129a..b79e9a2891 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.2.2'; +export const VERSION = '5.2.3'; export const PUBLIC_VERSION = '5'; From 012166ec3c2a4f199d0314444519096bc21d3715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Santiago=20C=C3=A9zar?= <48573316+santiagocezar@users.noreply.github.com> Date: Tue, 19 Nov 2024 13:48:01 -0300 Subject: [PATCH 172/404] fix: convert input value to number on hydration (#14349) * convert input value to number on hydration * add test * changeset --------- Co-authored-by: Rich Harris --- .changeset/wicked-grapes-flash.md | 5 +++++ .../client/dom/elements/bindings/input.js | 2 +- .../hydrate-modified-input-numeric/_config.js | 17 +++++++++++++++++ .../hydrate-modified-input-numeric/main.svelte | 6 ++++++ 4 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 .changeset/wicked-grapes-flash.md create mode 100644 packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-numeric/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-numeric/main.svelte diff --git a/.changeset/wicked-grapes-flash.md b/.changeset/wicked-grapes-flash.md new file mode 100644 index 0000000000..75ff68a728 --- /dev/null +++ b/.changeset/wicked-grapes-flash.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: coerce value to number when hydrating range/number input with changed value 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 e528d1699e..aec6f815a0 100644 --- a/packages/svelte/src/internal/client/dom/elements/bindings/input.js +++ b/packages/svelte/src/internal/client/dom/elements/bindings/input.js @@ -45,7 +45,7 @@ export function bind_value(input, get, set = get) { // If we are hydrating and the value has since changed, then use the update value // from the input instead. if (hydrating && input.defaultValue !== input.value) { - set(input.value); + set(is_numberlike_input(input) ? to_number(input.value) : input.value); return; } diff --git a/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-numeric/_config.js b/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-numeric/_config.js new file mode 100644 index 0000000000..f4b5037890 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-numeric/_config.js @@ -0,0 +1,17 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + skip_mode: ['client'], + + test({ assert, target, hydrate }) { + const input = /** @type {HTMLInputElement} */ (target.querySelector('input')); + input.value = '1'; + input.dispatchEvent(new window.Event('input')); + // Hydration shouldn't reset the value to empty + hydrate(); + flushSync(); + + assert.htmlEqual(target.innerHTML, '\n1 (number)'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-numeric/main.svelte b/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-numeric/main.svelte new file mode 100644 index 0000000000..a10f3cec1e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-numeric/main.svelte @@ -0,0 +1,6 @@ + + + +{value} ({typeof value}) From 6e8267f46277592f6f6afb6f77d62d3d9d740605 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Tue, 19 Nov 2024 16:48:30 +0000 Subject: [PATCH 173/404] fix: correctly update dynamic member expressions (#14359) * fix: output template effect for svg xlink attribute * mark subtree dynamic in MemberExpression visitor * don't treat attributes and text nodes differently * Update .changeset/serious-spiders-bake.md --------- Co-authored-by: Rich Harris --- .changeset/serious-spiders-bake.md | 5 +++++ .../phases/2-analyze/visitors/Identifier.js | 13 ++++--------- .../phases/2-analyze/visitors/MemberExpression.js | 3 +++ .../samples/inline-expressions-3/_config.js | 5 +++++ .../samples/inline-expressions-3/main.svelte | 9 +++++++++ .../samples/inline-expressions-3/sprites.js | 3 +++ .../_expected/client/index.svelte.js | 2 +- 7 files changed, 30 insertions(+), 10 deletions(-) create mode 100644 .changeset/serious-spiders-bake.md create mode 100644 packages/svelte/tests/runtime-legacy/samples/inline-expressions-3/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/inline-expressions-3/main.svelte create mode 100644 packages/svelte/tests/runtime-legacy/samples/inline-expressions-3/sprites.js diff --git a/.changeset/serious-spiders-bake.md b/.changeset/serious-spiders-bake.md new file mode 100644 index 0000000000..b1f9d84a3f --- /dev/null +++ b/.changeset/serious-spiders-bake.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: correctly update dynamic member expressions 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 635f939c75..042c467df1 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js @@ -126,16 +126,11 @@ export function Identifier(node, context) { } } - if (!can_inline && context.state.expression) { - context.state.expression.can_inline = false; - } + if (!can_inline) { + if (context.state.expression) { + context.state.expression.can_inline = false; + } - /** - * if the identifier is part of an expression tag of an attribute we want to check if it's inlinable - * before marking the subtree as dynamic. This is because if it's inlinable it will be inlined in the template - * directly making the whole thing actually static. - */ - if (!can_inline || !context.path.find((node) => node.type === 'Attribute')) { mark_subtree_dynamic(context.path); } } 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 adcc2da422..88adecbd35 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/MemberExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/MemberExpression.js @@ -4,6 +4,7 @@ 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 @@ -20,6 +21,8 @@ export function MemberExpression(node, context) { if (context.state.expression && !is_pure(node, context)) { context.state.expression.has_state = true; context.state.expression.can_inline = false; + + mark_subtree_dynamic(context.path); } if (!is_safe_identifier(node, context.state.scope)) { diff --git a/packages/svelte/tests/runtime-legacy/samples/inline-expressions-3/_config.js b/packages/svelte/tests/runtime-legacy/samples/inline-expressions-3/_config.js new file mode 100644 index 0000000000..c229f97e07 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/inline-expressions-3/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + html: `
+ import { sprites } from './sprites.js' + + +
+ +
diff --git a/packages/svelte/tests/runtime-legacy/samples/inline-expressions-3/sprites.js b/packages/svelte/tests/runtime-legacy/samples/inline-expressions-3/sprites.js new file mode 100644 index 0000000000..055fdbd59a --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/inline-expressions-3/sprites.js @@ -0,0 +1,3 @@ +export const sprites = { + a: 'test' +}; diff --git a/packages/svelte/tests/snapshot/samples/inline-module-vars/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/inline-module-vars/_expected/client/index.svelte.js index 75e632239e..4622624424 100644 --- a/packages/svelte/tests/snapshot/samples/inline-module-vars/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/inline-module-vars/_expected/client/index.svelte.js @@ -11,7 +11,7 @@ var root = $.template(` + {#each tiles as { x, y }} +
+ {/each} +
diff --git a/benchmarking/benchmarks/ssr/wrapper/wrapper_bench.js b/benchmarking/benchmarks/ssr/wrapper/wrapper_bench.js new file mode 100644 index 0000000000..ba0457b80e --- /dev/null +++ b/benchmarking/benchmarks/ssr/wrapper/wrapper_bench.js @@ -0,0 +1,36 @@ +import { render } from 'svelte/server'; +import { fastest_test, read_file, write } from '../../../utils.js'; +import { compile } from 'svelte/compiler'; + +const dir = `${process.cwd()}/benchmarking/benchmarks/ssr/wrapper`; + +async function compile_svelte() { + const output = compile(read_file(`${dir}/App.svelte`), { + generate: 'server' + }); + write(`${dir}/output/App.js`, output.js.code); + + const module = await import(`${dir}/output/App.js`); + + return module.default; +} + +export async function wrapper_bench() { + const App = await compile_svelte(); + // Do 3 loops to warm up JIT + for (let i = 0; i < 3; i++) { + render(App); + } + + const { timing } = await fastest_test(10, () => { + for (let i = 0; i < 100; i++) { + render(App); + } + }); + + return { + benchmark: 'wrapper_bench', + time: timing.time.toFixed(2), + gc_time: timing.gc_time.toFixed(2) + }; +} diff --git a/benchmarking/run.js b/benchmarking/run.js index 35a6ac307f..bd96b9c2dc 100644 --- a/benchmarking/run.js +++ b/benchmarking/run.js @@ -1,30 +1,53 @@ import * as $ from '../packages/svelte/src/internal/client/index.js'; -import { benchmarks } from './benchmarks.js'; +import { reactivity_benchmarks } from './benchmarks/reactivity/index.js'; +import { ssr_benchmarks } from './benchmarks/ssr/index.js'; let total_time = 0; let total_gc_time = 0; +const suites = [ + { benchmarks: reactivity_benchmarks, name: 'reactivity benchmarks' }, + { benchmarks: ssr_benchmarks, name: 'server-side rendering benchmarks' } +]; + // eslint-disable-next-line no-console -console.log('-- Benchmarking Started --'); +console.log('\x1b[1m', '-- Benchmarking Started --', '\x1b[0m'); $.push({}, true); try { - for (const benchmark of benchmarks) { - const results = await benchmark(); + for (const { benchmarks, name } of suites) { + let suite_time = 0; + let suite_gc_time = 0; + // eslint-disable-next-line no-console + console.log(`\nRunning ${name}...\n`); + + for (const benchmark of benchmarks) { + const results = await benchmark(); + // eslint-disable-next-line no-console + console.log(results); + total_time += Number(results.time); + total_gc_time += Number(results.gc_time); + suite_time += Number(results.time); + suite_gc_time += Number(results.gc_time); + } + + console.log(`\nFinished ${name}.\n`); + // eslint-disable-next-line no-console - console.log(results); - total_time += Number(results.time); - total_gc_time += Number(results.gc_time); + console.log({ + suite_time: suite_time.toFixed(2), + suite_gc_time: suite_gc_time.toFixed(2) + }); } } catch (e) { // eslint-disable-next-line no-console - console.error('-- Benchmarking Failed --'); + console.log('\x1b[1m', '\n-- Benchmarking Failed --\n', '\x1b[0m'); // eslint-disable-next-line no-console console.error(e); process.exit(1); } $.pop(); // eslint-disable-next-line no-console -console.log('-- Benchmarking Complete --'); +console.log('\x1b[1m', '\n-- Benchmarking Complete --\n', '\x1b[0m'); // eslint-disable-next-line no-console console.log({ total_time: total_time.toFixed(2), diff --git a/benchmarking/utils.js b/benchmarking/utils.js index db2fa753ee..684d2ee02b 100644 --- a/benchmarking/utils.js +++ b/benchmarking/utils.js @@ -1,5 +1,7 @@ import { performance, PerformanceObserver } from 'node:perf_hooks'; import v8 from 'v8-natives'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; // Credit to https://github.com/milomg/js-reactivity-benchmark for the logic for timing + GC tracking. @@ -96,3 +98,22 @@ export function assert(a) { throw new Error('Assertion failed'); } } + +/** + * @param {string} file + */ +export function read_file(file) { + return fs.readFileSync(file, 'utf-8').replace(/\r\n/g, '\n'); +} + +/** + * @param {string} file + * @param {string} contents + */ +export function write(file, contents) { + try { + fs.mkdirSync(path.dirname(file), { recursive: true }); + } catch {} + + fs.writeFileSync(file, contents); +} From 741106879bc60bb8b4e5daf3884032a04c803dd2 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Tue, 19 Nov 2024 18:11:30 +0000 Subject: [PATCH 175/404] fix: ensure internal cloning can work circular values (#14347) * fix: ensure internal cloning can work circular values * better fixc * 'original' feels slightly clearer than 'json_instance' * use an optional parameter, so we can omit it in most cases * Update packages/svelte/src/internal/shared/clone.js Co-authored-by: Rich Harris --------- Co-authored-by: Rich Harris --- .changeset/cool-trains-yawn.md | 5 ++++ packages/svelte/src/internal/shared/clone.js | 15 ++++++++++-- .../samples/inspect-recursive-2/_config.js | 23 +++++++++++++++++++ .../samples/inspect-recursive-2/main.svelte | 23 +++++++++++++++++++ 4 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 .changeset/cool-trains-yawn.md create mode 100644 packages/svelte/tests/runtime-runes/samples/inspect-recursive-2/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/inspect-recursive-2/main.svelte diff --git a/.changeset/cool-trains-yawn.md b/.changeset/cool-trains-yawn.md new file mode 100644 index 0000000000..d5198d118c --- /dev/null +++ b/.changeset/cool-trains-yawn.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure internal cloning can work circular values diff --git a/packages/svelte/src/internal/shared/clone.js b/packages/svelte/src/internal/shared/clone.js index bfdc9af263..cef14bc14f 100644 --- a/packages/svelte/src/internal/shared/clone.js +++ b/packages/svelte/src/internal/shared/clone.js @@ -49,9 +49,10 @@ export function snapshot(value, skip_warning = false) { * @param {Map>} cloned * @param {string} path * @param {string[]} paths + * @param {null | T} original The original value, if `value` was produced from a `toJSON` call * @returns {Snapshot} */ -function clone(value, cloned, path, paths) { +function clone(value, cloned, path, paths, original = null) { if (typeof value === 'object' && value !== null) { const unwrapped = cloned.get(value); if (unwrapped !== undefined) return unwrapped; @@ -63,6 +64,10 @@ function clone(value, cloned, path, paths) { const copy = /** @type {Snapshot} */ ([]); cloned.set(value, copy); + if (original !== null) { + cloned.set(original, copy); + } + for (let i = 0; i < value.length; i += 1) { copy.push(clone(value[i], cloned, DEV ? `${path}[${i}]` : path, paths)); } @@ -75,6 +80,10 @@ function clone(value, cloned, path, paths) { const copy = {}; cloned.set(value, copy); + if (original !== null) { + cloned.set(original, copy); + } + for (var key in value) { // @ts-expect-error copy[key] = clone(value[key], cloned, DEV ? `${path}.${key}` : path, paths); @@ -92,7 +101,9 @@ function clone(value, cloned, path, paths) { /** @type {T & { toJSON(): any } } */ (value).toJSON(), cloned, DEV ? `${path}.toJSON()` : path, - paths + paths, + // Associate the instance with the toJSON clone + value ); } } diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-recursive-2/_config.js b/packages/svelte/tests/runtime-runes/samples/inspect-recursive-2/_config.js new file mode 100644 index 0000000000..ab49697195 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/inspect-recursive-2/_config.js @@ -0,0 +1,23 @@ +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + }, + + async test({ assert, logs }) { + var a = { + a: {} + }; + a.a = a; + + var b = { + a: { + b: {} + } + }; + b.a.b = b; + + assert.deepEqual(logs, ['init', a, 'init', b]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-recursive-2/main.svelte b/packages/svelte/tests/runtime-runes/samples/inspect-recursive-2/main.svelte new file mode 100644 index 0000000000..f7874d2192 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/inspect-recursive-2/main.svelte @@ -0,0 +1,23 @@ + From 747d40833b024d2b5ea5cf0de8e6198cbd3af23c Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Tue, 19 Nov 2024 19:04:40 +0000 Subject: [PATCH 176/404] fix: ensure is_pure takes into account runes (#14333) * fix: ensure is_pure takes into account runes * feedback --- .changeset/slimy-islands-cry.md | 5 +++++ .../phases/2-analyze/visitors/shared/utils.js | 10 ++++++++- .../samples/inline-expressions-2/_config.js | 11 ---------- .../samples/inline-expressions/_config.js | 21 +++++++++++++++++++ .../samples/inline-expressions}/main.svelte | 1 + 5 files changed, 36 insertions(+), 12 deletions(-) create mode 100644 .changeset/slimy-islands-cry.md delete mode 100644 packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/inline-expressions/_config.js rename packages/svelte/tests/{runtime-legacy/samples/inline-expressions-2 => runtime-runes/samples/inline-expressions}/main.svelte (87%) diff --git a/.changeset/slimy-islands-cry.md b/.changeset/slimy-islands-cry.md new file mode 100644 index 0000000000..f7f34b60e1 --- /dev/null +++ b/.changeset/slimy-islands-cry.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure is_pure takes into account $effect.tracking() 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 2dec4361c8..8698174c6b 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 @@ -4,8 +4,10 @@ /** @import { Scope } from '../../../scope' */ /** @import { NodeLike } from '../../../../errors.js' */ import * as e from '../../../../errors.js'; -import { extract_identifiers, object } from '../../../../utils/ast.js'; +import { extract_identifiers } from '../../../../utils/ast.js'; import * as w from '../../../../warnings.js'; +import * as b from '../../../../utils/builders.js'; +import { get_rune } from '../../../scope.js'; /** * @param {AssignmentExpression | UpdateExpression} node @@ -184,6 +186,7 @@ export function is_pure(node, context) { if (node.type === 'Literal') { return true; } + if (node.type === 'CallExpression') { if (!is_pure(node.callee, context)) { return false; @@ -195,10 +198,15 @@ export function is_pure(node, context) { } return true; } + if (node.type !== 'Identifier' && node.type !== 'MemberExpression') { return false; } + if (get_rune(b.call(node), context.state.scope) === '$effect.tracking') { + return false; + } + /** @type {Expression | Super | null} */ let left = node; while (left.type === 'MemberExpression') { diff --git a/packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/_config.js b/packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/_config.js deleted file mode 100644 index 1869540b2c..0000000000 --- a/packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/_config.js +++ /dev/null @@ -1,11 +0,0 @@ -import { test } from '../../test'; - -export default test({ - html: ` -

Without text expression: 7.36

-

With text expression: 7.36

-

With text expression and function call: 7.36

-

With text expression and property access: 4

-

Hello name!

-

4

` -}); diff --git a/packages/svelte/tests/runtime-runes/samples/inline-expressions/_config.js b/packages/svelte/tests/runtime-runes/samples/inline-expressions/_config.js new file mode 100644 index 0000000000..c5d4a75379 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/inline-expressions/_config.js @@ -0,0 +1,21 @@ +import { test } from '../../test'; + +export default test({ + html: ` +

Without text expression: 7.36

+

With text expression: 7.36

+

With text expression and function call: 7.36

+

With text expression and property access: 4

+

Hello name!

+

4

+

Tracking: true

`, + + ssrHtml: ` +

Without text expression: 7.36

+

With text expression: 7.36

+

With text expression and function call: 7.36

+

With text expression and property access: 4

+

Hello name!

+

4

+

Tracking: false

` +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/main.svelte b/packages/svelte/tests/runtime-runes/samples/inline-expressions/main.svelte similarity index 87% rename from packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/main.svelte rename to packages/svelte/tests/runtime-runes/samples/inline-expressions/main.svelte index 78a325dcc4..38a97415d2 100644 --- a/packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/inline-expressions/main.svelte @@ -4,3 +4,4 @@

With text expression and property access: {"test".length}

Hello {('name').toUpperCase().toLowerCase()}!

{"test".length}

+

Tracking: {$effect.tracking()}

From 53cc60085e099352c04732f0653af03b63af624c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 19 Nov 2024 14:05:50 -0500 Subject: [PATCH 177/404] Version Packages (#14362) Co-authored-by: github-actions[bot] --- .changeset/cool-trains-yawn.md | 5 ----- .changeset/serious-spiders-bake.md | 5 ----- .changeset/slimy-islands-cry.md | 5 ----- .changeset/wicked-grapes-flash.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/cool-trains-yawn.md delete mode 100644 .changeset/serious-spiders-bake.md delete mode 100644 .changeset/slimy-islands-cry.md delete mode 100644 .changeset/wicked-grapes-flash.md diff --git a/.changeset/cool-trains-yawn.md b/.changeset/cool-trains-yawn.md deleted file mode 100644 index d5198d118c..0000000000 --- a/.changeset/cool-trains-yawn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure internal cloning can work circular values diff --git a/.changeset/serious-spiders-bake.md b/.changeset/serious-spiders-bake.md deleted file mode 100644 index b1f9d84a3f..0000000000 --- a/.changeset/serious-spiders-bake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: correctly update dynamic member expressions diff --git a/.changeset/slimy-islands-cry.md b/.changeset/slimy-islands-cry.md deleted file mode 100644 index f7f34b60e1..0000000000 --- a/.changeset/slimy-islands-cry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure is_pure takes into account $effect.tracking() diff --git a/.changeset/wicked-grapes-flash.md b/.changeset/wicked-grapes-flash.md deleted file mode 100644 index 75ff68a728..0000000000 --- a/.changeset/wicked-grapes-flash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: coerce value to number when hydrating range/number input with changed value diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index d36307c80c..f01f519150 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,17 @@ # svelte +## 5.2.4 + +### Patch Changes + +- fix: ensure internal cloning can work circular values ([#14347](https://github.com/sveltejs/svelte/pull/14347)) + +- fix: correctly update dynamic member expressions ([#14359](https://github.com/sveltejs/svelte/pull/14359)) + +- fix: ensure is_pure takes into account $effect.tracking() ([#14333](https://github.com/sveltejs/svelte/pull/14333)) + +- fix: coerce value to number when hydrating range/number input with changed value ([#14349](https://github.com/sveltejs/svelte/pull/14349)) + ## 5.2.3 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 50319c77aa..f6ba053398 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.2.3", + "version": "5.2.4", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index b79e9a2891..3a728f3d14 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.2.3'; +export const VERSION = '5.2.4'; export const PUBLIC_VERSION = '5'; From ce471310c49695550f8422ade46025f9818106c2 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 19 Nov 2024 16:31:22 -0500 Subject: [PATCH 178/404] chore: remove some unused code (#14363) --- .../visitors/ExportNamedDeclaration.js | 18 ------------------ 1 file changed, 18 deletions(-) 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 784d2fdd88..547f6ab9c7 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportNamedDeclaration.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportNamedDeclaration.js @@ -57,23 +57,5 @@ export function ExportNamedDeclaration(node, context) { } } } - - if (!context.state.ast_type /* .svelte.js module */ || context.state.ast_type === 'module') { - for (const specified of node.specifiers) { - if (specified.local.type !== 'Identifier') continue; - - const binding = context.state.scope.get(specified.local.name); - - if (!binding) continue; - - if (binding.kind === 'derived') { - e.derived_invalid_export(node); - } - - if ((binding.kind === 'state' || binding.kind === 'raw_state') && binding.reassigned) { - e.state_invalid_export(node); - } - } - } } } From 32a14538051a9ab382b467d4c5f772114809b7d1 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Tue, 19 Nov 2024 22:38:36 +0100 Subject: [PATCH 179/404] fix: include method definitions in class private fields (#14365) * fix: include method definitions in class private fields * Update packages/svelte/tests/runtime-runes/samples/class-disabinguate-private-method-definition/_config.js --------- Co-authored-by: Rich Harris --- .changeset/unlucky-icons-sit.md | 5 +++++ .../phases/3-transform/client/visitors/ClassBody.js | 2 +- .../_config.js | 3 +++ .../main.svelte | 8 ++++++++ 4 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 .changeset/unlucky-icons-sit.md create mode 100644 packages/svelte/tests/runtime-runes/samples/class-disabinguate-private-method-definition/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/class-disabinguate-private-method-definition/main.svelte diff --git a/.changeset/unlucky-icons-sit.md b/.changeset/unlucky-icons-sit.md new file mode 100644 index 0000000000..7fc14cf435 --- /dev/null +++ b/.changeset/unlucky-icons-sit.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: include method definitions in class private fields 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 2d832b9df1..11a524d33c 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 @@ -28,7 +28,7 @@ export function ClassBody(node, context) { for (const definition of node.body) { if ( - definition.type === 'PropertyDefinition' && + (definition.type === 'PropertyDefinition' || definition.type === 'MethodDefinition') && (definition.key.type === 'Identifier' || definition.key.type === 'PrivateIdentifier' || definition.key.type === 'Literal') diff --git a/packages/svelte/tests/runtime-runes/samples/class-disabinguate-private-method-definition/_config.js b/packages/svelte/tests/runtime-runes/samples/class-disabinguate-private-method-definition/_config.js new file mode 100644 index 0000000000..f47bee71df --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-disabinguate-private-method-definition/_config.js @@ -0,0 +1,3 @@ +import { test } from '../../test'; + +export default test({}); diff --git a/packages/svelte/tests/runtime-runes/samples/class-disabinguate-private-method-definition/main.svelte b/packages/svelte/tests/runtime-runes/samples/class-disabinguate-private-method-definition/main.svelte new file mode 100644 index 0000000000..92f2540498 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-disabinguate-private-method-definition/main.svelte @@ -0,0 +1,8 @@ + From 4c98c2e4a6aaa54650cc834e109a0e2dabd5a574 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 20 Nov 2024 15:52:03 +0100 Subject: [PATCH 180/404] chore: consolidate checks for never-static attributes (#14372) We're using string checks in various places, better to have it encapsulated in one function --- .../phases/2-analyze/visitors/RegularElement.js | 6 ++---- .../3-transform/client/visitors/RegularElement.js | 4 ++-- .../3-transform/client/visitors/shared/fragment.js | 3 ++- packages/svelte/src/utils.js | 11 +++++++++++ 4 files changed, 17 insertions(+), 7 deletions(-) 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 60bd1dd0c5..d5964f9ae1 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js @@ -1,6 +1,6 @@ /** @import { AST } from '#compiler' */ /** @import { Context } from '../types' */ -import { is_mathml, is_svg, is_void } from '../../../../utils.js'; +import { cannot_be_set_statically, is_mathml, is_svg, is_void } from '../../../../utils.js'; import { is_tag_valid_with_ancestor, is_tag_valid_with_parent @@ -77,9 +77,7 @@ export function RegularElement(node, context) { if ( node.attributes.some( - (attribute) => - attribute.type === 'Attribute' && - (attribute.name === 'autofocus' || attribute.name === 'muted') + (attribute) => attribute.type === 'Attribute' && cannot_be_set_statically(attribute.name) ) ) { mark_subtree_dynamic(context.path); 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 455327a712..19948464d2 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 @@ -5,6 +5,7 @@ /** @import { Scope } from '../../../scope' */ import { escape_html } from '../../../../../escaping.js'; import { + cannot_be_set_statically, is_boolean_attribute, is_dom_property, is_load_error_element, @@ -262,8 +263,7 @@ export function RegularElement(node, context) { if ( !is_custom_element && - attribute.name !== 'autofocus' && - attribute.name !== 'muted' && + !cannot_be_set_statically(attribute.name) && (attribute.value === true || is_text_attribute(attribute)) ) { const name = get_attribute_name(node, attribute); 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 ac6e0f8f9f..102988781a 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,6 +3,7 @@ /** @import { Scope } from '../../../../scope.js' */ /** @import { ComponentContext } from '../../types' */ import { escape_html } from '../../../../../../escaping.js'; +import { cannot_be_set_statically } from '../../../../../../utils.js'; import { is_event_attribute } from '../../../../../utils/ast.js'; import * as b from '../../../../../utils/builders.js'; import { build_template_chunk, build_update } from './utils.js'; @@ -142,7 +143,7 @@ function is_static_element(node) { return false; } - if (attribute.name === 'autofocus' || attribute.name === 'muted') { + if (cannot_be_set_statically(attribute.name)) { return false; } diff --git a/packages/svelte/src/utils.js b/packages/svelte/src/utils.js index 60ec364e6f..84c7ca1efb 100644 --- a/packages/svelte/src/utils.js +++ b/packages/svelte/src/utils.js @@ -222,6 +222,17 @@ export function is_dom_property(name) { return DOM_PROPERTIES.includes(name); } +const NON_STATIC_PROPERTIES = ['autofocus', 'muted']; + +/** + * Returns `true` if the given attribute cannot be set through the template + * string, i.e. needs some kind of JavaScript handling to work. + * @param {string} name + */ +export function cannot_be_set_statically(name) { + return NON_STATIC_PROPERTIES.includes(name); +} + /** * Subset of delegated events which should be passive by default. * These two are already passive via browser defaults on window, document and body. From 4dfa0e31fe70174538ca47efe1b276ca13b2cd80 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 20 Nov 2024 16:14:33 +0100 Subject: [PATCH 181/404] fix: tighten up `export default` validation (#14368) through #14363 I noticed our `export default` validation wasn't quite right: - missed checking for derived/state exports - the "cannot have a default export" error was only thrown if you did `export default` from the instance script, but it shouldn't matter from which component part you export it; it's never ok --- .changeset/new-houses-roll.md | 5 ++++ .../visitors/ExportDefaultDeclaration.js | 9 +++++-- .../2-analyze/visitors/ExportSpecifier.js | 25 ++----------------- .../phases/2-analyze/visitors/shared/utils.js | 21 +++++++++++++++- .../_config.js | 10 ++++++++ .../main.svelte.js | 5 ++++ .../export-default-state-indirect/_config.js | 10 ++++++++ .../main.svelte.js | 7 ++++++ .../samples/default-export-module/errors.json | 14 +++++++++++ .../default-export-module/input.svelte | 3 +++ 10 files changed, 83 insertions(+), 26 deletions(-) create mode 100644 .changeset/new-houses-roll.md create mode 100644 packages/svelte/tests/compiler-errors/samples/export-default-derived-state-indirect/_config.js create mode 100644 packages/svelte/tests/compiler-errors/samples/export-default-derived-state-indirect/main.svelte.js create mode 100644 packages/svelte/tests/compiler-errors/samples/export-default-state-indirect/_config.js create mode 100644 packages/svelte/tests/compiler-errors/samples/export-default-state-indirect/main.svelte.js create mode 100644 packages/svelte/tests/validator/samples/default-export-module/errors.json create mode 100644 packages/svelte/tests/validator/samples/default-export-module/input.svelte diff --git a/.changeset/new-houses-roll.md b/.changeset/new-houses-roll.md new file mode 100644 index 0000000000..a2b8370e06 --- /dev/null +++ b/.changeset/new-houses-roll.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: tighten up `export default` validation diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportDefaultDeclaration.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportDefaultDeclaration.js index 0a7461f155..768f1c6305 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportDefaultDeclaration.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportDefaultDeclaration.js @@ -1,13 +1,18 @@ -/** @import { ExportDefaultDeclaration, Node } from 'estree' */ +/** @import { ExportDefaultDeclaration } from 'estree' */ /** @import { Context } from '../types' */ import * as e from '../../../errors.js'; +import { validate_export } from './shared/utils.js'; /** * @param {ExportDefaultDeclaration} node * @param {Context} context */ export function ExportDefaultDeclaration(node, context) { - if (context.state.ast_type === 'instance') { + if (!context.state.ast_type /* .svelte.js module */) { + if (node.declaration.type === 'Identifier') { + validate_export(node, context.state.scope, node.declaration.name); + } + } else { e.module_illegal_default_export(node); } 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 d0d1ccf932..cfb24970de 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportSpecifier.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportSpecifier.js @@ -1,8 +1,6 @@ -/** @import { ExportSpecifier, Node } from 'estree' */ -/** @import { Binding } from '#compiler' */ +/** @import { ExportSpecifier } from 'estree' */ /** @import { Context } from '../types' */ -/** @import { Scope } from '../../scope' */ -import * as e from '../../../errors.js'; +import { validate_export } from './shared/utils.js'; /** * @param {ExportSpecifier} node @@ -30,22 +28,3 @@ export function ExportSpecifier(node, context) { validate_export(node, context.state.scope, local_name); } } - -/** - * - * @param {Node} node - * @param {Scope} scope - * @param {string} name - */ -function validate_export(node, scope, name) { - const binding = scope.get(name); - if (!binding) return; - - if (binding.kind === 'derived') { - e.derived_invalid_export(node); - } - - if ((binding.kind === 'state' || binding.kind === 'raw_state') && binding.reassigned) { - e.state_invalid_export(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 8698174c6b..e265637c40 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, Pattern, PrivateIdentifier, Super, UpdateExpression, VariableDeclarator } from 'estree' */ +/** @import { AssignmentExpression, Expression, Literal, Node, Pattern, PrivateIdentifier, Super, UpdateExpression, VariableDeclarator } from 'estree' */ /** @import { AST, Binding } from '#compiler' */ /** @import { AnalysisState, Context } from '../../types' */ /** @import { Scope } from '../../../scope' */ @@ -263,3 +263,22 @@ export function validate_identifier_name(binding, function_depth) { } } } + +/** + * Checks that the exported name is not a derived or reassigned state variable. + * @param {Node} node + * @param {Scope} scope + * @param {string} name + */ +export function validate_export(node, scope, name) { + const binding = scope.get(name); + if (!binding) return; + + if (binding.kind === 'derived') { + e.derived_invalid_export(node); + } + + if ((binding.kind === 'state' || binding.kind === 'raw_state') && binding.reassigned) { + e.state_invalid_export(node); + } +} diff --git a/packages/svelte/tests/compiler-errors/samples/export-default-derived-state-indirect/_config.js b/packages/svelte/tests/compiler-errors/samples/export-default-derived-state-indirect/_config.js new file mode 100644 index 0000000000..4a36211769 --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/export-default-derived-state-indirect/_config.js @@ -0,0 +1,10 @@ +import { test } from '../../test'; + +export default test({ + error: { + code: 'derived_invalid_export', + message: + 'Cannot export derived state from a module. To expose the current derived value, export a function returning its value', + position: [61, 83] + } +}); diff --git a/packages/svelte/tests/compiler-errors/samples/export-default-derived-state-indirect/main.svelte.js b/packages/svelte/tests/compiler-errors/samples/export-default-derived-state-indirect/main.svelte.js new file mode 100644 index 0000000000..64a794492b --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/export-default-derived-state-indirect/main.svelte.js @@ -0,0 +1,5 @@ +let count = $state(0); + +const double = $derived(count * 2); + +export default double; diff --git a/packages/svelte/tests/compiler-errors/samples/export-default-state-indirect/_config.js b/packages/svelte/tests/compiler-errors/samples/export-default-state-indirect/_config.js new file mode 100644 index 0000000000..99e4faec25 --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/export-default-state-indirect/_config.js @@ -0,0 +1,10 @@ +import { test } from '../../test'; + +export default test({ + error: { + code: 'state_invalid_export', + message: + "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", + position: [93, 118] + } +}); diff --git a/packages/svelte/tests/compiler-errors/samples/export-default-state-indirect/main.svelte.js b/packages/svelte/tests/compiler-errors/samples/export-default-state-indirect/main.svelte.js new file mode 100644 index 0000000000..8e52f76533 --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/export-default-state-indirect/main.svelte.js @@ -0,0 +1,7 @@ +let primitive = $state('nope'); + +export function update_primitive() { + primitive = 'yep'; +} + +export default primitive; diff --git a/packages/svelte/tests/validator/samples/default-export-module/errors.json b/packages/svelte/tests/validator/samples/default-export-module/errors.json new file mode 100644 index 0000000000..9fd2bb7df5 --- /dev/null +++ b/packages/svelte/tests/validator/samples/default-export-module/errors.json @@ -0,0 +1,14 @@ +[ + { + "code": "module_illegal_default_export", + "message": "A component cannot have a default export", + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 19 + } + } +] diff --git a/packages/svelte/tests/validator/samples/default-export-module/input.svelte b/packages/svelte/tests/validator/samples/default-export-module/input.svelte new file mode 100644 index 0000000000..2aeeabb2e1 --- /dev/null +++ b/packages/svelte/tests/validator/samples/default-export-module/input.svelte @@ -0,0 +1,3 @@ + From 7bd1cdf4271078c977cc39497cbe545a9de4dd62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Crozatier?= <48696601+fcrozatier@users.noreply.github.com> Date: Wed, 20 Nov 2024 16:14:56 +0100 Subject: [PATCH 182/404] feat: add content-visibility: auto state change event (#14373) * add contentvisibilityautostatechange event * changeset * Update .changeset/kind-horses-lay.md * Update .changeset/kind-horses-lay.md --------- Co-authored-by: Rich Harris --- .changeset/kind-horses-lay.md | 5 +++++ packages/svelte/elements.d.ts | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 .changeset/kind-horses-lay.md diff --git a/.changeset/kind-horses-lay.md b/.changeset/kind-horses-lay.md new file mode 100644 index 0000000000..d08bdde6c4 --- /dev/null +++ b/.changeset/kind-horses-lay.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +add `contentvisibilityautostatechange` event to element definitions diff --git a/packages/svelte/elements.d.ts b/packages/svelte/elements.d.ts index 8746b29e25..8b2d388bf8 100644 --- a/packages/svelte/elements.d.ts +++ b/packages/svelte/elements.d.ts @@ -60,6 +60,10 @@ export type AnimationEventHandler = EventHandler = EventHandler; export type MessageEventHandler = EventHandler; export type ToggleEventHandler = EventHandler; +export type ContentVisibilityAutoStateChangeEventHandler = EventHandler< + ContentVisibilityAutoStateChangeEvent, + T +>; export type FullAutoFill = | AutoFill @@ -157,6 +161,20 @@ export interface DOMAttributes { ontoggle?: ToggleEventHandler | undefined | null; ontogglecapture?: ToggleEventHandler | undefined | null; + // Content visibility Events + 'on:contentvisibilityautostatechange'?: + | ContentVisibilityAutoStateChangeEventHandler + | undefined + | null; + oncontentvisibilityautostatechange?: + | ContentVisibilityAutoStateChangeEventHandler + | undefined + | null; + oncontentvisibilityautostatechangecapture?: + | ContentVisibilityAutoStateChangeEventHandler + | undefined + | null; + // Keyboard Events 'on:keydown'?: KeyboardEventHandler | undefined | null; onkeydown?: KeyboardEventHandler | undefined | null; From 811c8d32ebcd38501ec5acf1eabe461693bc24d7 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Wed, 20 Nov 2024 15:19:17 +0000 Subject: [PATCH 183/404] fix: correctly handle srcObject attribute on video elements (#14369) * fix: correctly handle srcObject attribute on video elements * remove side-effect * side-effects agin * side-effects agin * better fix --- .changeset/bright-jokes-bow.md | 5 +++++ packages/svelte/src/utils.js | 6 ++++-- .../samples/video-src-object/_config.js | 12 ++++++++++++ .../samples/video-src-object/main.svelte | 10 ++++++++++ 4 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 .changeset/bright-jokes-bow.md create mode 100644 packages/svelte/tests/runtime-runes/samples/video-src-object/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/video-src-object/main.svelte diff --git a/.changeset/bright-jokes-bow.md b/.changeset/bright-jokes-bow.md new file mode 100644 index 0000000000..f70076b869 --- /dev/null +++ b/.changeset/bright-jokes-bow.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: correctly handle srcObject attribute on video elements diff --git a/packages/svelte/src/utils.js b/packages/svelte/src/utils.js index 84c7ca1efb..919660fd6a 100644 --- a/packages/svelte/src/utils.js +++ b/packages/svelte/src/utils.js @@ -192,7 +192,8 @@ const ATTRIBUTE_ALIASES = { ismap: 'isMap', nomodule: 'noModule', playsinline: 'playsInline', - readonly: 'readOnly' + readonly: 'readOnly', + srcobject: 'srcObject' }; /** @@ -212,7 +213,8 @@ const DOM_PROPERTIES = [ 'readOnly', 'value', 'inert', - 'volume' + 'volume', + 'srcObject' ]; /** diff --git a/packages/svelte/tests/runtime-runes/samples/video-src-object/_config.js b/packages/svelte/tests/runtime-runes/samples/video-src-object/_config.js new file mode 100644 index 0000000000..a31b98cac6 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/video-src-object/_config.js @@ -0,0 +1,12 @@ +import { test } from '../../test'; + +export default test({ + html: ``, + + test({ assert, target }) { + const video = target.querySelector('video'); + + // @ts-ignore + assert.deepEqual(video?.srcObject, {}); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/video-src-object/main.svelte b/packages/svelte/tests/runtime-runes/samples/video-src-object/main.svelte new file mode 100644 index 0000000000..f4059b028a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/video-src-object/main.svelte @@ -0,0 +1,10 @@ + + + + From f616c2205399090965d6cf11e9e9b7d5c542fafc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 20 Nov 2024 10:30:25 -0500 Subject: [PATCH 184/404] Version Packages (#14366) Co-authored-by: github-actions[bot] --- .changeset/bright-jokes-bow.md | 5 ----- .changeset/kind-horses-lay.md | 5 ----- .changeset/new-houses-roll.md | 5 ----- .changeset/unlucky-icons-sit.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/bright-jokes-bow.md delete mode 100644 .changeset/kind-horses-lay.md delete mode 100644 .changeset/new-houses-roll.md delete mode 100644 .changeset/unlucky-icons-sit.md diff --git a/.changeset/bright-jokes-bow.md b/.changeset/bright-jokes-bow.md deleted file mode 100644 index f70076b869..0000000000 --- a/.changeset/bright-jokes-bow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: correctly handle srcObject attribute on video elements diff --git a/.changeset/kind-horses-lay.md b/.changeset/kind-horses-lay.md deleted file mode 100644 index d08bdde6c4..0000000000 --- a/.changeset/kind-horses-lay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -add `contentvisibilityautostatechange` event to element definitions diff --git a/.changeset/new-houses-roll.md b/.changeset/new-houses-roll.md deleted file mode 100644 index a2b8370e06..0000000000 --- a/.changeset/new-houses-roll.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: tighten up `export default` validation diff --git a/.changeset/unlucky-icons-sit.md b/.changeset/unlucky-icons-sit.md deleted file mode 100644 index 7fc14cf435..0000000000 --- a/.changeset/unlucky-icons-sit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: include method definitions in class private fields diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index f01f519150..f5b581ab0e 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,17 @@ # svelte +## 5.2.5 + +### Patch Changes + +- fix: correctly handle srcObject attribute on video elements ([#14369](https://github.com/sveltejs/svelte/pull/14369)) + +- add `contentvisibilityautostatechange` event to element definitions ([#14373](https://github.com/sveltejs/svelte/pull/14373)) + +- fix: tighten up `export default` validation ([#14368](https://github.com/sveltejs/svelte/pull/14368)) + +- fix: include method definitions in class private fields ([#14365](https://github.com/sveltejs/svelte/pull/14365)) + ## 5.2.4 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index f6ba053398..2205990fff 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.2.4", + "version": "5.2.5", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 3a728f3d14..5014c88702 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.2.4'; +export const VERSION = '5.2.5'; export const PUBLIC_VERSION = '5'; From 9d12fd1a01d7020855122a8bfb5a35a3dd4e1c8f Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Wed, 20 Nov 2024 17:46:53 +0000 Subject: [PATCH 185/404] chore: remove template expression inlining (#14374) * chore: remove template expression inlining * missed some * fix * feedback * feedback * Update packages/svelte/src/compiler/phases/3-transform/client/utils.js Co-authored-by: Rich Harris * Update packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js Co-authored-by: Rich Harris * Update packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js Co-authored-by: Rich Harris * Update packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js Co-authored-by: Rich Harris * Update packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js Co-authored-by: Rich Harris * Update packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js Co-authored-by: Rich Harris * fix * Update .changeset/calm-mice-perform.md Co-authored-by: Rich Harris --------- Co-authored-by: Rich Harris --- .changeset/calm-mice-perform.md | 5 + .../phases/2-analyze/visitors/Attribute.js | 9 +- .../2-analyze/visitors/CallExpression.js | 2 - .../2-analyze/visitors/ExpressionTag.js | 5 + .../phases/2-analyze/visitors/Identifier.js | 17 +--- .../2-analyze/visitors/MemberExpression.js | 3 - .../2-analyze/visitors/RegularElement.js | 10 +- .../visitors/TaggedTemplateExpression.js | 1 - .../phases/3-transform/client/utils.js | 10 +- .../3-transform/client/visitors/Fragment.js | 10 +- .../client/visitors/RegularElement.js | 72 ++++----------- .../client/visitors/shared/fragment.js | 65 +++---------- .../client/visitors/shared/utils.js | 92 ++++++++----------- packages/svelte/src/compiler/phases/nodes.js | 3 +- packages/svelte/src/compiler/types/index.d.ts | 2 - packages/svelte/src/internal/client/index.js | 1 - .../samples/slot-non-identifier/output.svelte | 2 +- .../_expected/client/index.svelte.js | 17 ---- .../_expected/server/index.svelte.js | 12 --- .../samples/inline-module-vars/index.svelte | 20 ---- 20 files changed, 97 insertions(+), 261 deletions(-) create mode 100644 .changeset/calm-mice-perform.md delete mode 100644 packages/svelte/tests/snapshot/samples/inline-module-vars/_expected/client/index.svelte.js delete mode 100644 packages/svelte/tests/snapshot/samples/inline-module-vars/_expected/server/index.svelte.js delete mode 100644 packages/svelte/tests/snapshot/samples/inline-module-vars/index.svelte diff --git a/.changeset/calm-mice-perform.md b/.changeset/calm-mice-perform.md new file mode 100644 index 0000000000..f43c7fcf0e --- /dev/null +++ b/.changeset/calm-mice-perform.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: remove template expression inlining 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 ca0ea55d1f..6c050d966a 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js @@ -1,7 +1,7 @@ /** @import { ArrowFunctionExpression, Expression, FunctionDeclaration, FunctionExpression } from 'estree' */ /** @import { AST, DelegatedEvent, SvelteNode } from '#compiler' */ /** @import { Context } from '../types' */ -import { is_boolean_attribute, is_capture_event, is_delegated } from '../../../../utils.js'; +import { cannot_be_set_statically, is_capture_event, is_delegated } from '../../../../utils.js'; import { get_attribute_chunks, get_attribute_expression, @@ -30,12 +30,12 @@ export function Attribute(node, context) { } } - if (node.name.startsWith('on')) { + if (is_event_attribute(node)) { mark_subtree_dynamic(context.path); } - if (parent.type === 'RegularElement' && is_boolean_attribute(node.name.toLowerCase())) { - node.metadata.expression.can_inline = false; + if (cannot_be_set_statically(node.name)) { + mark_subtree_dynamic(context.path); } if (node.value !== true) { @@ -51,7 +51,6 @@ export function Attribute(node, context) { node.metadata.expression.has_state ||= chunk.metadata.expression.has_state; node.metadata.expression.has_call ||= chunk.metadata.expression.has_call; - node.metadata.expression.can_inline &&= chunk.metadata.expression.can_inline; } if (is_event_attribute(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 fe3f638a69..c7ade4856b 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js @@ -179,8 +179,6 @@ export function CallExpression(node, context) { if (!is_pure(node.callee, context) || context.state.expression.dependencies.size > 0) { context.state.expression.has_call = true; context.state.expression.has_state = true; - context.state.expression.can_inline = false; - mark_subtree_dynamic(context.path); } } } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExpressionTag.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExpressionTag.js index f59b7fc569..32c8d2ca36 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExpressionTag.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExpressionTag.js @@ -2,6 +2,7 @@ /** @import { Context } from '../types' */ import { is_tag_valid_with_parent } from '../../../../html-tree-validation.js'; import * as e from '../../../errors.js'; +import { mark_subtree_dynamic } from './shared/fragment.js'; /** * @param {AST.ExpressionTag} node @@ -14,5 +15,9 @@ export function ExpressionTag(node, context) { } } + // TODO ideally we wouldn't do this here, we'd just do it on encountering + // an `Identifier` within the tag. But we currently need to handle `{42}` etc + mark_subtree_dynamic(context.path); + context.next({ ...context.state, expression: node.metadata.expression }); } 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 042c467df1..79dccd5a7c 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js @@ -1,4 +1,5 @@ /** @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'; @@ -19,6 +20,8 @@ export function Identifier(node, context) { return; } + mark_subtree_dynamic(context.path); + // If we are using arguments outside of a function, then throw an error if ( node.name === 'arguments' && @@ -84,12 +87,6 @@ export function Identifier(node, context) { } } - // no binding means global, and we can't inline e.g. `{location}` - // because it could change between component renders. if there _is_ a - // binding and it is outside module scope, the expression cannot - // be inlined (TODO allow inlining in more cases - e.g. primitive consts) - let can_inline = !!binding && !binding.scope.parent && binding.kind === 'normal'; - if (binding) { if (context.state.expression) { context.state.expression.dependencies.add(binding); @@ -125,12 +122,4 @@ export function Identifier(node, context) { w.reactive_declaration_module_script_dependency(node); } } - - if (!can_inline) { - if (context.state.expression) { - context.state.expression.can_inline = false; - } - - mark_subtree_dynamic(context.path); - } } 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 88adecbd35..1cc20c96da 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/MemberExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/MemberExpression.js @@ -20,9 +20,6 @@ export function MemberExpression(node, context) { if (context.state.expression && !is_pure(node, context)) { context.state.expression.has_state = true; - context.state.expression.can_inline = false; - - mark_subtree_dynamic(context.path); } if (!is_safe_identifier(node, context.state.scope)) { 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 d5964f9ae1..7d1c4aaeaa 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js @@ -1,6 +1,6 @@ /** @import { AST } from '#compiler' */ /** @import { Context } from '../types' */ -import { cannot_be_set_statically, is_mathml, is_svg, is_void } from '../../../../utils.js'; +import { is_mathml, is_svg, is_void } from '../../../../utils.js'; import { is_tag_valid_with_ancestor, is_tag_valid_with_parent @@ -75,14 +75,6 @@ export function RegularElement(node, context) { node.attributes.push(create_attribute('value', child.start, child.end, [child])); } - if ( - node.attributes.some( - (attribute) => attribute.type === 'Attribute' && cannot_be_set_statically(attribute.name) - ) - ) { - mark_subtree_dynamic(context.path); - } - const binding = context.state.scope.get(node.name); if ( binding !== null && 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 724b9af311..eacb8a342a 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/TaggedTemplateExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/TaggedTemplateExpression.js @@ -10,7 +10,6 @@ export function TaggedTemplateExpression(node, context) { if (context.state.expression && !is_pure(node.tag, context)) { context.state.expression.has_call = true; context.state.expression.has_state = true; - context.state.expression.can_inline = false; } if (node.tag.type === 'Identifier') { 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 910f173f79..0b49d18ee9 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/utils.js @@ -3,16 +3,16 @@ /** @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 { - PROPS_IS_BINDABLE, - PROPS_IS_IMMUTABLE, PROPS_IS_LAZY_INITIAL, + PROPS_IS_IMMUTABLE, PROPS_IS_RUNES, - PROPS_IS_UPDATED + PROPS_IS_UPDATED, + PROPS_IS_BINDABLE } from '../../../../constants.js'; import { dev } from '../../../state.js'; -import { extract_identifiers, is_simple_expression } from '../../../utils/ast.js'; -import * as b from '../../../utils/builders.js'; import { get_value } from './visitors/shared/declarations.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 111516ce0a..0e6ea29614 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 @@ -141,14 +141,14 @@ export function Fragment(node, context) { const id = b.id(context.state.scope.generate('fragment')); const use_space_template = - trimmed.every((node) => node.type === 'Text' || node.type === 'ExpressionTag') && - trimmed.some((node) => node.type === 'ExpressionTag' && !node.metadata.expression.can_inline); + trimmed.some((node) => node.type === 'ExpressionTag') && + trimmed.every((node) => node.type === 'Text' || node.type === 'ExpressionTag'); if (use_space_template) { // special case — we can use `$.text` instead of creating a unique template const id = b.id(context.state.scope.generate('text')); - process_children(trimmed, () => id, null, { + process_children(trimmed, () => id, false, { ...context, state }); @@ -158,12 +158,12 @@ export function Fragment(node, context) { } else { if (is_standalone) { // no need to create a template, we can just use the existing block's anchor - process_children(trimmed, () => b.id('$$anchor'), null, { ...context, state }); + process_children(trimmed, () => b.id('$$anchor'), false, { ...context, state }); } else { /** @type {(is_text: boolean) => Expression} */ const expression = (is_text) => b.call('$.first_child', id, is_text && b.true); - process_children(trimmed, expression, null, { ...context, state }); + process_children(trimmed, expression, false, { ...context, state }); let flags = TEMPLATE_FRAGMENT; 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 19948464d2..85df92e8bf 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,9 +1,8 @@ -/** @import { Expression, ExpressionStatement, Identifier, Literal, MemberExpression, ObjectExpression, 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' */ /** @import { Scope } from '../../../scope' */ -import { escape_html } from '../../../../../escaping.js'; import { cannot_be_set_statically, is_boolean_attribute, @@ -11,6 +10,7 @@ import { is_load_error_element, is_void } from '../../../../../utils.js'; +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'; @@ -18,13 +18,12 @@ 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 { + get_attribute_name, build_attribute_value, build_class_directives, - build_set_attributes, build_style_directives, - get_attribute_name + build_set_attributes } from './shared/element.js'; -import { visit_event_attribute } from './shared/events.js'; import { process_children } from './shared/fragment.js'; import { build_render_statement, @@ -32,6 +31,7 @@ import { build_update, build_update_assignment } from './shared/utils.js'; +import { visit_event_attribute } from './shared/events.js'; /** * @param {AST.RegularElement} node @@ -352,32 +352,30 @@ export function RegularElement(node, context) { // special case — if an element that only contains text, we don't need // to descend into it if the text is non-reactive - const is_text = trimmed.every((node) => node.type === 'Text' || node.type === 'ExpressionTag'); - // in the rare case that we have static text that can't be inlined // (e.g. `{location}`), set `textContent` programmatically const use_text_content = - is_text && + trimmed.every((node) => node.type === 'Text' || node.type === 'ExpressionTag') && trimmed.every((node) => node.type === 'Text' || !node.metadata.expression.has_state) && - trimmed.some((node) => node.type === 'ExpressionTag' && !node.metadata.expression.can_inline); + trimmed.some((node) => node.type === 'ExpressionTag'); if (use_text_content) { - let { value } = build_template_chunk(trimmed, context.visit, child_state); - child_state.init.push( - b.stmt(b.assignment('=', b.member(context.state.node, 'textContent'), value)) + b.stmt( + b.assignment( + '=', + b.member(context.state.node, 'textContent'), + build_template_chunk(trimmed, context.visit, child_state).value + ) + ) ); } else { /** @type {Expression} */ let arg = context.state.node; // If `hydrate_node` is set inside the element, we need to reset it - // after the element has been hydrated (we don't need to reset if it's been inlined) - let needs_reset = !trimmed.every( - (node) => - node.type === 'Text' || - (node.type === 'ExpressionTag' && node.metadata.expression.can_inline) - ); + // after the element has been hydrated + let needs_reset = trimmed.some((node) => node.type !== 'Text'); // The same applies if it's a `