From ab3290fa870838a1f8c8baddc5d093dfa3265280 Mon Sep 17 00:00:00 2001 From: eyalkutz <45904301+eyalkutz@users.noreply.github.com> Date: Sun, 12 Jan 2025 20:52:27 +0200 Subject: [PATCH 001/280] Remove out-of-date note from CONTRIBUTING.md (#14986) --- CONTRIBUTING.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dd7bbb476e..f7d15f905e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -62,8 +62,6 @@ When [opening a new issue](https://github.com/sveltejs/svelte/issues/new/choose) ## Pull requests -> HEADS UP: Svelte 5 will likely change a lot on the compiler. For that reason, please don't open PRs that are large in scope, touch more than a couple of files etc. In other words, bug fixes are fine, but big feature PRs will likely not be merged. - ### Proposing a change If you would like to request a new feature or enhancement but are not yet thinking about opening a pull request, you can also file an issue with [feature template](https://github.com/sveltejs/svelte/issues/new?template=feature_request.yml). From 36ef59df1f35635d53947b357d4211f3bf1f713a Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Mon, 13 Jan 2025 21:14:07 +0000 Subject: [PATCH 002/280] chore: improve test cases (#14984) * chore: improve test cases * lint --- .../camel-case-attribute/_config.js | 7 ++-- .../custom-method/_config.js | 4 ++- .../element-effect-context/_config.js | 3 +- .../_config.js | 6 ++-- .../_config.js | 6 ++-- .../_config.js | 6 ++-- .../_config.js | 6 ++-- .../action-custom-event-handler/_config.js | 6 ++-- .../samples/action-this/_config.js | 4 ++- .../samples/await-mutate-array/_config.js | 5 ++- .../await-then-destruct-object-if/_config.js | 3 ++ .../samples/before-render-chain/_config.js | 6 ++-- .../_config.js | 6 ++-- .../binding-contenteditable-text/_config.js | 6 ++-- .../_config.js | 6 ++-- .../binding-select-in-yield/_config.js | 15 +++++---- .../samples/bindings-coalesced/_config.js | 6 ++-- .../component-props-mutated/_config.js | 6 ++-- .../samples/component-slot-let-g/_config.js | 6 ++-- .../samples/destructured-props-2/_config.js | 3 +- .../samples/destructured-props-3/_config.js | 4 ++- .../samples/destructured-props-5/_config.js | 3 +- .../destructuring-assignment-array/_config.js | 7 ++-- .../samples/key-block-expression-2/_config.js | 15 ++++++--- .../samples/prop-const/_config.js | 6 ++-- .../samples/props-reactive-b/_config.js | 9 +++-- .../_config.js | 6 ++-- .../_config.js | 33 +++++++++++-------- .../reactive-value-function/_config.js | 6 ++-- .../reactive-values-text-node/_config.js | 3 ++ .../samples/spread-own-props/_config.js | 6 ++-- .../_config.js | 8 ++--- .../_config.js | 7 ++-- .../samples/store-unreferenced/_config.js | 6 ++-- .../samples/window-event-custom/_config.js | 6 ++-- .../samples/derived-unowned-2/_config.js | 3 +- .../samples/derived-unowned-5/_config.js | 3 +- .../samples/runes-from-func/_config.js | 3 +- 38 files changed, 164 insertions(+), 86 deletions(-) diff --git a/packages/svelte/tests/runtime-browser/custom-elements-samples/camel-case-attribute/_config.js b/packages/svelte/tests/runtime-browser/custom-elements-samples/camel-case-attribute/_config.js index ba3be3c25b..b9bfdd7782 100644 --- a/packages/svelte/tests/runtime-browser/custom-elements-samples/camel-case-attribute/_config.js +++ b/packages/svelte/tests/runtime-browser/custom-elements-samples/camel-case-attribute/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../assert'; const tick = () => Promise.resolve(); @@ -14,8 +15,7 @@ export default test({ el.setAttribute('camel-case', 'universe'); el.setAttribute('an-array', '[3,4]'); el.setAttribute('camelcase2', 'Hi'); - await tick(); - await tick(); + flushSync(); assert.htmlEqual(el.shadowRoot.innerHTML, '

Hi universe!

3

4

'); assert.htmlEqual( target.innerHTML, @@ -25,8 +25,7 @@ export default test({ el.camelCase = 'galaxy'; el.camelCase2 = 'Hey'; el.anArray = [5, 6]; - await tick(); - await tick(); + flushSync(); assert.htmlEqual(el.shadowRoot.innerHTML, '

Hey galaxy!

5

6

'); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-browser/custom-elements-samples/custom-method/_config.js b/packages/svelte/tests/runtime-browser/custom-elements-samples/custom-method/_config.js index dbdf006be5..4314926a94 100644 --- a/packages/svelte/tests/runtime-browser/custom-elements-samples/custom-method/_config.js +++ b/packages/svelte/tests/runtime-browser/custom-elements-samples/custom-method/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../assert'; const tick = () => Promise.resolve(); @@ -8,7 +9,8 @@ export default test({ /** @type {any} */ const el = target.querySelector('custom-element'); - await el.updateFoo(42); + el.updateFoo(42); + flushSync(); const p = el.shadowRoot.querySelector('p'); assert.equal(p.textContent, '42'); diff --git a/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/_config.js b/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/_config.js index 3cf7a66df1..7f2ba9f331 100644 --- a/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/_config.js +++ b/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/_config.js @@ -1,4 +1,5 @@ import { test } from '../../assert'; +import { flushSync } from 'svelte'; const tick = () => Promise.resolve(); export default test({ @@ -16,7 +17,7 @@ export default test({ assert.equal(p.innerHTML, 'false'); button.click(); - await tick(); + flushSync(); assert.equal(button.innerHTML, '1'); assert.equal(p.innerHTML, 'false'); diff --git a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-in-each-destructured/_config.js b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-in-each-destructured/_config.js index 650d0ec360..fe13d43bc8 100644 --- a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-in-each-destructured/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-in-each-destructured/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -10,12 +11,13 @@ export default test({

second:

`, - async test({ assert, component, target, window }) { + test({ assert, component, target, window }) { const event = new window.MouseEvent('click'); const buttons = target.querySelectorAll('button'); - await buttons[1].dispatchEvent(event); + buttons[1].dispatchEvent(event); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-in-each/_config.js b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-in-each/_config.js index 21fb167873..83b14cb8a3 100644 --- a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-in-each/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-in-each/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -10,12 +11,13 @@ export default test({

fromState:

`, - async test({ assert, component, target, window }) { + test({ assert, component, target, window }) { const event = new window.MouseEvent('click'); const buttons = target.querySelectorAll('button'); - await buttons[1].dispatchEvent(event); + buttons[1].dispatchEvent(event); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-node-context/_config.js b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-node-context/_config.js index a2edd90f8b..9276ca8a59 100644 --- a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-node-context/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-node-context/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { ok, test } from '../../test'; export default test({ @@ -5,13 +6,14 @@ export default test({ html: '', - async test({ assert, target, window }) { + test({ assert, target, window }) { const event = new window.MouseEvent('click'); const button = target.querySelector('button'); ok(button); - await button.dispatchEvent(event); + button.dispatchEvent(event); + flushSync(); assert.htmlEqual(target.innerHTML, ''); } diff --git a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-with-context/_config.js b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-with-context/_config.js index c4d84fc2b3..2e6aa4dfee 100644 --- a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-with-context/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-with-context/_config.js @@ -1,9 +1,10 @@ +import { flushSync } from 'svelte'; import { ok, test } from '../../test'; export default test({ html: '', - async test({ assert, target, window }) { + test({ assert, target, window }) { const event = new window.MouseEvent('click', { clientX: 42, clientY: 42 @@ -12,7 +13,8 @@ export default test({ const button = target.querySelector('button'); ok(button); - await button.dispatchEvent(event); + button.dispatchEvent(event); + flushSync(); assert.htmlEqual(target.innerHTML, ''); } diff --git a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler/_config.js b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler/_config.js index 279171692d..3769a8bd81 100644 --- a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler/_config.js @@ -1,9 +1,10 @@ +import { flushSync } from 'svelte'; import { ok, test } from '../../test'; export default test({ html: '', - async test({ assert, target, window }) { + test({ assert, target, window }) { const event = new window.MouseEvent('click', { clientX: 42, clientY: 42 @@ -12,7 +13,8 @@ export default test({ const button = target.querySelector('button'); ok(button); - await button.dispatchEvent(event); + button.dispatchEvent(event); + flushSync(); assert.htmlEqual(target.innerHTML, ''); } diff --git a/packages/svelte/tests/runtime-legacy/samples/action-this/_config.js b/packages/svelte/tests/runtime-legacy/samples/action-this/_config.js index d296fc5a28..0ab9305ade 100644 --- a/packages/svelte/tests/runtime-legacy/samples/action-this/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/action-this/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { ok, test } from '../../test'; export default test({ @@ -8,7 +9,8 @@ export default test({ const click = new window.MouseEvent('click'); assert.htmlEqual(target.innerHTML, ''); - await button.dispatchEvent(click); + button.dispatchEvent(click); + flushSync(); assert.htmlEqual(target.innerHTML, ''); } }); diff --git a/packages/svelte/tests/runtime-legacy/samples/await-mutate-array/_config.js b/packages/svelte/tests/runtime-legacy/samples/await-mutate-array/_config.js index 67ea2cb0a3..70268a0e1d 100644 --- a/packages/svelte/tests/runtime-legacy/samples/await-mutate-array/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/await-mutate-array/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -11,7 +12,9 @@ export default test({ const [b1] = target.querySelectorAll('button'); b1.click(); - await Promise.resolve(); + Promise.resolve(); + flushSync(); + assert.htmlEqual( target.innerHTML, `\n-------\n` diff --git a/packages/svelte/tests/runtime-legacy/samples/await-then-destruct-object-if/_config.js b/packages/svelte/tests/runtime-legacy/samples/await-then-destruct-object-if/_config.js index e823c21c9a..af04467749 100644 --- a/packages/svelte/tests/runtime-legacy/samples/await-then-destruct-object-if/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/await-then-destruct-object-if/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -11,6 +12,7 @@ export default test({ async test({ assert, component, target }) { await (component.thePromise = Promise.resolve({ result: 1 })); + flushSync(); assert.htmlEqual( target.innerHTML, @@ -21,6 +23,7 @@ export default test({ ); await new Promise((resolve) => setTimeout(resolve, 1)); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/before-render-chain/_config.js b/packages/svelte/tests/runtime-legacy/samples/before-render-chain/_config.js index 9364bd9f9e..580df5ca81 100644 --- a/packages/svelte/tests/runtime-legacy/samples/before-render-chain/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/before-render-chain/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -9,8 +10,9 @@ export default test({ 1 `, - async test({ assert, component, target }) { - await component.list.update(); + test({ assert, component, target }) { + component.list.update(); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-contenteditable-text-initial/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-contenteditable-text-initial/_config.js index e2a28e8dd9..83ad9c2558 100644 --- a/packages/svelte/tests/runtime-legacy/samples/binding-contenteditable-text-initial/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/binding-contenteditable-text-initial/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { ok, test } from '../../test'; export default test({ @@ -18,7 +19,7 @@ export default test({

hello

`, - async test({ assert, component, target, window }) { + test({ assert, component, target, window }) { assert.equal(component.name, 'world'); const el = target.querySelector('editor'); @@ -27,7 +28,8 @@ export default test({ const event = new window.Event('input'); el.textContent = 'everybody'; - await el.dispatchEvent(event); + el.dispatchEvent(event); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-contenteditable-text/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-contenteditable-text/_config.js index afecc5b3cd..494c338bfe 100644 --- a/packages/svelte/tests/runtime-legacy/samples/binding-contenteditable-text/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/binding-contenteditable-text/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { ok, test } from '../../test'; export default test({ @@ -10,7 +11,7 @@ export default test({

hello world

`, - async test({ assert, component, target, window }) { + test({ assert, component, target, window }) { const el = target.querySelector('editor'); ok(el); assert.equal(el.textContent, 'world'); @@ -18,7 +19,8 @@ export default test({ const event = new window.Event('input'); el.textContent = 'everybody'; - await el.dispatchEvent(event); + el.dispatchEvent(event); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-input-checkbox-indeterminate/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-input-checkbox-indeterminate/_config.js index 631423feaf..eba77322a1 100644 --- a/packages/svelte/tests/runtime-legacy/samples/binding-input-checkbox-indeterminate/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/binding-input-checkbox-indeterminate/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { ok, test } from '../../test'; export default test({ @@ -17,7 +18,7 @@ export default test({

indeterminate? true

`, - async test({ assert, component, target, window }) { + test({ assert, component, target, window }) { const input = target.querySelector('input'); ok(input); @@ -28,7 +29,8 @@ export default test({ input.checked = true; input.indeterminate = false; - await input.dispatchEvent(event); + input.dispatchEvent(event); + flushSync(); assert.equal(component.indeterminate, false); assert.equal(component.checked, true); diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-select-in-yield/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-select-in-yield/_config.js index f7e5445072..b8f6a9a74a 100644 --- a/packages/svelte/tests/runtime-legacy/samples/binding-select-in-yield/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/binding-select-in-yield/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { ok, test } from '../../test'; export default test({ @@ -7,8 +8,9 @@ export default test({ return { letter: 'b' }; }, - async test({ assert, component, target, window }) { - await component.modal.toggle(); + test({ assert, component, target, window }) { + component.modal.toggle(); + flushSync(); assert.htmlEqual( target.innerHTML, @@ -28,7 +30,8 @@ export default test({ const change = new window.MouseEvent('change'); select.options[2].selected = true; - await select.dispatchEvent(change); + select.dispatchEvent(change); + flushSync(); assert.equal(component.letter, 'c'); assert.deepEqual( @@ -49,9 +52,9 @@ export default test({ ` ); - await component.modal.toggle(); - await component.modal.toggle(); - await Promise.resolve(); + component.modal.toggle(); + component.modal.toggle(); + flushSync(); select = target.querySelector('select'); ok(select); diff --git a/packages/svelte/tests/runtime-legacy/samples/bindings-coalesced/_config.js b/packages/svelte/tests/runtime-legacy/samples/bindings-coalesced/_config.js index 85e1faeb73..3bc5d845a1 100644 --- a/packages/svelte/tests/runtime-legacy/samples/bindings-coalesced/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/bindings-coalesced/_config.js @@ -1,7 +1,8 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ - async test({ assert, component }) { + test({ assert, component }) { const { foo, p } = component; /** @type {string[]} */ @@ -13,7 +14,8 @@ export default test({ } }); - await foo.double(); + foo.double(); + flushSync(); assert.deepEqual(values, ['6']); } diff --git a/packages/svelte/tests/runtime-legacy/samples/component-props-mutated/_config.js b/packages/svelte/tests/runtime-legacy/samples/component-props-mutated/_config.js index 91d0aaa0d3..4a5c102ba2 100644 --- a/packages/svelte/tests/runtime-legacy/samples/component-props-mutated/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/component-props-mutated/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; const data = { @@ -15,9 +16,10 @@ export default test({ html: '

hello

', - async test({ assert, component, target }) { + test({ assert, component, target }) { data.message = 'goodbye'; - await component.$set({ data }); + component.$set({ data }); + flushSync(); assert.htmlEqual(target.innerHTML, '

goodbye

'); } diff --git a/packages/svelte/tests/runtime-legacy/samples/component-slot-let-g/_config.js b/packages/svelte/tests/runtime-legacy/samples/component-slot-let-g/_config.js index db9afae1b0..f3f5e04be1 100644 --- a/packages/svelte/tests/runtime-legacy/samples/component-slot-let-g/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/component-slot-let-g/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -5,7 +6,7 @@ export default test({ 1 0 `, - async test({ assert, target, component, window }) { + test({ assert, target, component, window }) { component.x = 2; assert.htmlEqual( @@ -17,7 +18,8 @@ export default test({ ); const span = target.querySelector('span'); - await span?.dispatchEvent(new window.MouseEvent('click', { bubbles: true })); + span?.dispatchEvent(new window.MouseEvent('click', { bubbles: true })); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/destructured-props-2/_config.js b/packages/svelte/tests/runtime-legacy/samples/destructured-props-2/_config.js index 950ee4f1ff..0f3fcedce5 100644 --- a/packages/svelte/tests/runtime-legacy/samples/destructured-props-2/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/destructured-props-2/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -10,7 +11,7 @@ export default test({ async test({ component, assert, target }) { await component.update(); - await Promise.resolve(); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/destructured-props-3/_config.js b/packages/svelte/tests/runtime-legacy/samples/destructured-props-3/_config.js index 9ec2810e71..c78b84ec8c 100644 --- a/packages/svelte/tests/runtime-legacy/samples/destructured-props-3/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/destructured-props-3/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -8,7 +9,8 @@ export default test({ `, async test({ component, target, assert }) { await component.update(); - await Promise.resolve(); + flushSync(); + assert.htmlEqual( target.innerHTML, ` diff --git a/packages/svelte/tests/runtime-legacy/samples/destructured-props-5/_config.js b/packages/svelte/tests/runtime-legacy/samples/destructured-props-5/_config.js index 8f7a544bfe..103ce64d7c 100644 --- a/packages/svelte/tests/runtime-legacy/samples/destructured-props-5/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/destructured-props-5/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -10,7 +11,7 @@ export default test({ async test({ component, assert, target }) { await component.update(); - await Promise.resolve(); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/destructuring-assignment-array/_config.js b/packages/svelte/tests/runtime-legacy/samples/destructuring-assignment-array/_config.js index 2df8753114..9c09581097 100644 --- a/packages/svelte/tests/runtime-legacy/samples/destructuring-assignment-array/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/destructuring-assignment-array/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -10,9 +11,9 @@ export default test({ `, - async test({ assert, component, target }) { - await component.swap(0, 1); - await Promise.resolve(); + test({ assert, component, target }) { + component.swap(0, 1); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/key-block-expression-2/_config.js b/packages/svelte/tests/runtime-legacy/samples/key-block-expression-2/_config.js index a661ec2596..99fa84e2a9 100644 --- a/packages/svelte/tests/runtime-legacy/samples/key-block-expression-2/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/key-block-expression-2/_config.js @@ -1,19 +1,26 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ html: '
3
', - async test({ assert, component, target }) { + test({ assert, component, target }) { const div = target.querySelector('div'); - await component.mutate(); + component.mutate(); + flushSync(); + assert.htmlEqual(target.innerHTML, '
5
'); assert.strictEqual(div, target.querySelector('div')); - await component.reassign(); + component.reassign(); + flushSync(); + assert.htmlEqual(target.innerHTML, '
7
'); assert.strictEqual(div, target.querySelector('div')); - await component.changeKey(); + component.changeKey(); + flushSync(); + assert.htmlEqual(target.innerHTML, '
7
'); assert.notStrictEqual(div, target.querySelector('div')); } diff --git a/packages/svelte/tests/runtime-legacy/samples/prop-const/_config.js b/packages/svelte/tests/runtime-legacy/samples/prop-const/_config.js index 040b911cc6..804ee53342 100644 --- a/packages/svelte/tests/runtime-legacy/samples/prop-const/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/prop-const/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -10,11 +11,12 @@ export default test({

b: 2

`, - async test({ assert, component, target }) { - await component.$set({ + test({ assert, component, target }) { + component.$set({ a: 5, b: 6 }); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/props-reactive-b/_config.js b/packages/svelte/tests/runtime-legacy/samples/props-reactive-b/_config.js index b92d81bd53..91133f0bf4 100644 --- a/packages/svelte/tests/runtime-legacy/samples/props-reactive-b/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/props-reactive-b/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -11,8 +12,9 @@ export default test({

c: 3

`, - async test({ assert, component, target }) { - await component.$set({ a: 4 }); + test({ assert, component, target }) { + component.$set({ a: 4 }); + flushSync(); assert.htmlEqual( target.innerHTML, @@ -23,7 +25,8 @@ export default test({ ` ); - await component.$set({ b: 5 }); + component.$set({ b: 5 }); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/reactive-assignment-in-complex-declaration-with-store-3/_config.js b/packages/svelte/tests/runtime-legacy/samples/reactive-assignment-in-complex-declaration-with-store-3/_config.js index 2325d17cd5..1a67e46b38 100644 --- a/packages/svelte/tests/runtime-legacy/samples/reactive-assignment-in-complex-declaration-with-store-3/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/reactive-assignment-in-complex-declaration-with-store-3/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; import { store } from './store.js'; @@ -6,10 +7,9 @@ export default test({ before_test() { store.reset(); }, - async test({ assert, target }) { + test({ assert, target }) { store.set(42); - - await Promise.resolve(); + flushSync(); assert.htmlEqual(target.innerHTML, '

42

'); diff --git a/packages/svelte/tests/runtime-legacy/samples/reactive-value-dependency-not-referenced/_config.js b/packages/svelte/tests/runtime-legacy/samples/reactive-value-dependency-not-referenced/_config.js index f2f7fe92b1..3b31ee766e 100644 --- a/packages/svelte/tests/runtime-legacy/samples/reactive-value-dependency-not-referenced/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/reactive-value-dependency-not-referenced/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -6,29 +7,35 @@ export default test({

42

`, - async test({ assert, component, target }) { - await component.updateStore(undefined); - await Promise.resolve(); + test({ assert, component, target }) { + component.updateStore(undefined); + flushSync(); + assert.htmlEqual(target.innerHTML, '

42

'); - await component.updateStore(33); - await Promise.resolve(); + component.updateStore(33); + flushSync(); + assert.htmlEqual(target.innerHTML, '

33

42

'); - await component.updateStore(undefined); - await Promise.resolve(); + component.updateStore(undefined); + flushSync(); + assert.htmlEqual(target.innerHTML, '

42

'); - await component.updateVar(undefined); - await Promise.resolve(); + component.updateVar(undefined); + flushSync(); + assert.htmlEqual(target.innerHTML, '

'); - await component.updateVar(33); - await Promise.resolve(); + component.updateVar(33); + flushSync(); + assert.htmlEqual(target.innerHTML, '

33

'); - await component.updateVar(undefined); - await Promise.resolve(); + component.updateVar(undefined); + flushSync(); + assert.htmlEqual(target.innerHTML, '

'); } }); diff --git a/packages/svelte/tests/runtime-legacy/samples/reactive-value-function/_config.js b/packages/svelte/tests/runtime-legacy/samples/reactive-value-function/_config.js index cbcb19d95e..5317ab496f 100644 --- a/packages/svelte/tests/runtime-legacy/samples/reactive-value-function/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/reactive-value-function/_config.js @@ -1,10 +1,12 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ html: '1-2', - async test({ assert, component, target }) { - await component.update(); + test({ assert, component, target }) { + component.update(); + flushSync(); assert.htmlEqual(target.innerHTML, '3-4'); } diff --git a/packages/svelte/tests/runtime-legacy/samples/reactive-values-text-node/_config.js b/packages/svelte/tests/runtime-legacy/samples/reactive-values-text-node/_config.js index e05c8c80dd..e97a046f68 100644 --- a/packages/svelte/tests/runtime-legacy/samples/reactive-values-text-node/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/reactive-values-text-node/_config.js @@ -1,5 +1,6 @@ import { test } from '../../test'; import { create_deferred } from '../../../helpers'; +import { flushSync } from 'svelte'; /** @type {ReturnType} */ let deferred; @@ -17,6 +18,8 @@ export default test({ async test({ assert, target }) { await deferred.promise; + flushSync(); + assert.htmlEqual( target.innerHTML, ` diff --git a/packages/svelte/tests/runtime-legacy/samples/spread-own-props/_config.js b/packages/svelte/tests/runtime-legacy/samples/spread-own-props/_config.js index 708e3615f1..7486055440 100644 --- a/packages/svelte/tests/runtime-legacy/samples/spread-own-props/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/spread-own-props/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -17,13 +18,14 @@ export default test({

quux: core

`, - async test({ assert, component, target }) { - await component.$set({ + test({ assert, component, target }) { + component.$set({ foo: 'wut', baz: 40 + 3, qux: `this is a ${'rather boring'} string`, quux: 'heart' }); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/store-auto-subscribe-in-reactive-declaration-2/_config.js b/packages/svelte/tests/runtime-legacy/samples/store-auto-subscribe-in-reactive-declaration-2/_config.js index 8e0886c668..d380150e55 100644 --- a/packages/svelte/tests/runtime-legacy/samples/store-auto-subscribe-in-reactive-declaration-2/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/store-auto-subscribe-in-reactive-declaration-2/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -6,10 +7,9 @@ export default test({
Hello World
`, - async test({ assert, component, target }) { - await component.update_value('Hi Svelte'); - await Promise.resolve(); - await Promise.resolve(); + test({ assert, component, target }) { + component.update_value('Hi Svelte'); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/store-increment-updates-reactive/_config.js b/packages/svelte/tests/runtime-legacy/samples/store-increment-updates-reactive/_config.js index 1c81f7c4e8..89ff695d9d 100644 --- a/packages/svelte/tests/runtime-legacy/samples/store-increment-updates-reactive/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/store-increment-updates-reactive/_config.js @@ -1,10 +1,13 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ html: '0', - async test({ assert, component, target }) { - await component.increment(); + test({ assert, component, target }) { + component.increment(); + flushSync(); + assert.htmlEqual(target.innerHTML, '1'); } }); diff --git a/packages/svelte/tests/runtime-legacy/samples/store-unreferenced/_config.js b/packages/svelte/tests/runtime-legacy/samples/store-unreferenced/_config.js index 808ed899d3..7096f5d1fe 100644 --- a/packages/svelte/tests/runtime-legacy/samples/store-unreferenced/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/store-unreferenced/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; import { count } from './store.js'; @@ -8,8 +9,9 @@ export default test({ count.set(0); }, - async test({ assert, component, target }) { - await component.increment(); + test({ assert, component, target }) { + component.increment(); + flushSync(); assert.htmlEqual(target.innerHTML, '

count: 1

'); } diff --git a/packages/svelte/tests/runtime-legacy/samples/window-event-custom/_config.js b/packages/svelte/tests/runtime-legacy/samples/window-event-custom/_config.js index 32b1c65154..f6b7a8af42 100644 --- a/packages/svelte/tests/runtime-legacy/samples/window-event-custom/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/window-event-custom/_config.js @@ -1,14 +1,16 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ html: '

escaped: false

', - async test({ assert, target, window }) { + test({ assert, target, window }) { const event = new window.KeyboardEvent('keydown', { key: 'Escape' }); - await window.dispatchEvent(event); + window.dispatchEvent(event); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-runes/samples/derived-unowned-2/_config.js b/packages/svelte/tests/runtime-runes/samples/derived-unowned-2/_config.js index 3604000543..3ca98bb0c6 100644 --- a/packages/svelte/tests/runtime-runes/samples/derived-unowned-2/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/derived-unowned-2/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -7,7 +8,7 @@ export default test({ async test({ assert, target }) { await Promise.resolve(); - await Promise.resolve(); + flushSync(); assert.htmlEqual( target.innerHTML, '
d2: 3,4,5
d3: 3,4,5
d4: 3,4,5
' diff --git a/packages/svelte/tests/runtime-runes/samples/derived-unowned-5/_config.js b/packages/svelte/tests/runtime-runes/samples/derived-unowned-5/_config.js index 20c3cc112e..5f38394d06 100644 --- a/packages/svelte/tests/runtime-runes/samples/derived-unowned-5/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/derived-unowned-5/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -5,7 +6,7 @@ export default test({ // The test has a bunch of queueMicrotasks await Promise.resolve(); await Promise.resolve(); - await Promise.resolve(); + flushSync(); assert.htmlEqual(target.innerHTML, `
Zeeba Neighba
`); } diff --git a/packages/svelte/tests/runtime-runes/samples/runes-from-func/_config.js b/packages/svelte/tests/runtime-runes/samples/runes-from-func/_config.js index 7d3dd9993f..5ed7579c62 100644 --- a/packages/svelte/tests/runtime-runes/samples/runes-from-func/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/runes-from-func/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -5,7 +6,7 @@ export default test({ async test({ assert, target }) { await Promise.resolve(); - await Promise.resolve(); + flushSync(); assert.htmlEqual(target.innerHTML, `1`); } }); From 8a1acac084096d9c9bd15a1356d3251383e8d258 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Mon, 13 Jan 2025 22:17:39 +0100 Subject: [PATCH 003/280] fix: never consider inert boundary effects (#14999) * fix: never consider inert boundary effects * chore: inline bitwise --- .changeset/healthy-hairs-run.md | 5 +++++ .../src/internal/client/reactivity/effects.js | 5 +++-- .../samples/error-boundary-21/Child.svelte | 3 +++ .../samples/error-boundary-21/_config.js | 17 +++++++++++++++++ .../samples/error-boundary-21/main.svelte | 14 ++++++++++++++ 5 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 .changeset/healthy-hairs-run.md create mode 100644 packages/svelte/tests/runtime-runes/samples/error-boundary-21/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/error-boundary-21/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/error-boundary-21/main.svelte diff --git a/.changeset/healthy-hairs-run.md b/.changeset/healthy-hairs-run.md new file mode 100644 index 0000000000..5f0ccd5234 --- /dev/null +++ b/.changeset/healthy-hairs-run.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: never consider inert boundary effects diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 16f076edde..149cbd2d38 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -35,7 +35,8 @@ import { INSPECT_EFFECT, HEAD_EFFECT, MAYBE_DIRTY, - EFFECT_HAS_DERIVED + EFFECT_HAS_DERIVED, + BOUNDARY_EFFECT } from '../constants.js'; import { set } from './sources.js'; import * as e from '../errors.js'; @@ -142,7 +143,7 @@ function create_effect(type, fn, sync, push = true) { effect.first === null && effect.nodes_start === null && effect.teardown === null && - (effect.f & EFFECT_HAS_DERIVED) === 0; + (effect.f & (EFFECT_HAS_DERIVED | BOUNDARY_EFFECT)) === 0; if (!inert && !is_root && push) { if (parent_effect !== null) { diff --git a/packages/svelte/tests/runtime-runes/samples/error-boundary-21/Child.svelte b/packages/svelte/tests/runtime-runes/samples/error-boundary-21/Child.svelte new file mode 100644 index 0000000000..ea60542af9 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/error-boundary-21/Child.svelte @@ -0,0 +1,3 @@ + diff --git a/packages/svelte/tests/runtime-runes/samples/error-boundary-21/_config.js b/packages/svelte/tests/runtime-runes/samples/error-boundary-21/_config.js new file mode 100644 index 0000000000..e301f83e60 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/error-boundary-21/_config.js @@ -0,0 +1,17 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + html: '
0
', + mode: ['client'], + test({ assert, target }) { + let btn = target.querySelector('button'); + let div = target.querySelector('div'); + + flushSync(() => { + btn?.click(); + }); + + assert.equal(div?.innerHTML, `1`); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/error-boundary-21/main.svelte b/packages/svelte/tests/runtime-runes/samples/error-boundary-21/main.svelte new file mode 100644 index 0000000000..ed3140b1ef --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/error-boundary-21/main.svelte @@ -0,0 +1,14 @@ + + + + + + + {#snippet failed()} +
{count}
+ {/snippet} +
From efa5acf24e040160172550eb5f7fa7ca9390f2e0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 17:18:43 -0500 Subject: [PATCH 004/280] Version Packages (#14970) Co-authored-by: github-actions[bot] --- .changeset/healthy-hairs-run.md | 5 ----- .changeset/hot-kings-shout.md | 5 ----- .changeset/spicy-insects-check.md | 5 ----- .changeset/tender-apples-scream.md | 5 ----- packages/svelte/CHANGELOG.md | 12 ++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 7 files changed, 14 insertions(+), 22 deletions(-) delete mode 100644 .changeset/healthy-hairs-run.md delete mode 100644 .changeset/hot-kings-shout.md delete mode 100644 .changeset/spicy-insects-check.md delete mode 100644 .changeset/tender-apples-scream.md diff --git a/.changeset/healthy-hairs-run.md b/.changeset/healthy-hairs-run.md deleted file mode 100644 index 5f0ccd5234..0000000000 --- a/.changeset/healthy-hairs-run.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: never consider inert boundary effects diff --git a/.changeset/hot-kings-shout.md b/.changeset/hot-kings-shout.md deleted file mode 100644 index afba164abf..0000000000 --- a/.changeset/hot-kings-shout.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: store access on component destroy diff --git a/.changeset/spicy-insects-check.md b/.changeset/spicy-insects-check.md deleted file mode 100644 index b998d36400..0000000000 --- a/.changeset/spicy-insects-check.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: correctly transform `pre` with no content diff --git a/.changeset/tender-apples-scream.md b/.changeset/tender-apples-scream.md deleted file mode 100644 index 836bdaffdf..0000000000 --- a/.changeset/tender-apples-scream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: wrap each block expression in derived to encapsulate effects diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 6023c6d4e1..4f3b9a168a 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,17 @@ # svelte +## 5.17.4 + +### Patch Changes + +- fix: never consider inert boundary effects ([#14999](https://github.com/sveltejs/svelte/pull/14999)) + +- fix: store access on component destroy ([#14968](https://github.com/sveltejs/svelte/pull/14968)) + +- fix: correctly transform `pre` with no content ([#14973](https://github.com/sveltejs/svelte/pull/14973)) + +- fix: wrap each block expression in derived to encapsulate effects ([#14967](https://github.com/sveltejs/svelte/pull/14967)) + ## 5.17.3 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 47aec9ee93..f426b97be4 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.17.3", + "version": "5.17.4", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 0fd7d2f039..69a8833938 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.17.3'; +export const VERSION = '5.17.4'; export const PUBLIC_VERSION = '5'; From a129592e5b248a734a68da6e9028941803a3d063 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Tue, 14 Jan 2025 01:36:33 +0100 Subject: [PATCH 005/280] feat: allow const tag inside `svelte:boundary` (#14993) * feat: allow const tag inside `svelte:boundary` * chore: add better test * docs: update docs for `@const` --- .changeset/curvy-lies-rush.md | 5 +++++ .../docs/03-template-syntax/09-@const.md | 2 +- .../phases/2-analyze/visitors/ConstTag.js | 1 + .../client/visitors/SvelteBoundary.js | 21 +++++++++++++++---- .../const-tag-boundary/FlakyComponent.svelte | 3 +++ .../samples/const-tag-boundary/_config.js | 17 +++++++++++++++ .../samples/const-tag-boundary/main.svelte | 14 +++++++++++++ .../errors.json | 1 + .../input.svelte | 11 ++++++++++ 9 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 .changeset/curvy-lies-rush.md create mode 100644 packages/svelte/tests/runtime-runes/samples/const-tag-boundary/FlakyComponent.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/const-tag-boundary/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/const-tag-boundary/main.svelte create mode 100644 packages/svelte/tests/validator/samples/const-tag-placement-svelte-boundary/errors.json create mode 100644 packages/svelte/tests/validator/samples/const-tag-placement-svelte-boundary/input.svelte diff --git a/.changeset/curvy-lies-rush.md b/.changeset/curvy-lies-rush.md new file mode 100644 index 0000000000..25c6ffc1d9 --- /dev/null +++ b/.changeset/curvy-lies-rush.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +feat: allow const tag inside `svelte:boundary` diff --git a/documentation/docs/03-template-syntax/09-@const.md b/documentation/docs/03-template-syntax/09-@const.md index f4bde77c23..c42d3560fd 100644 --- a/documentation/docs/03-template-syntax/09-@const.md +++ b/documentation/docs/03-template-syntax/09-@const.md @@ -11,4 +11,4 @@ The `{@const ...}` tag defines a local constant. {/each} ``` -`{@const}` is only allowed as an immediate child of a block — `{#if ...}`, `{#each ...}`, `{#snippet ...}` and so on — or a ``. +`{@const}` is only allowed as an immediate child of a block — `{#if ...}`, `{#each ...}`, `{#snippet ...}` and so on — a `` or a ` a.type === 'Attribute' && a.name === 'slot'))) ) { diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js index ef9f6bd798..325485d4c0 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js @@ -33,21 +33,34 @@ export function SvelteBoundary(node, context) { const nodes = []; /** @type {Statement[]} */ - const snippet_statements = []; + const external_statements = []; + + const snippets_visits = []; // Capture the `failed` implicit snippet prop for (const child of node.fragment.nodes) { if (child.type === 'SnippetBlock' && child.expression.name === 'failed') { + // we need to delay the visit of the snippets in case they access a ConstTag that is declared + // after the snippets so that the visitor for the const tag can be updated + snippets_visits.push(() => { + /** @type {Statement[]} */ + const init = []; + context.visit(child, { ...context.state, init }); + props.properties.push(b.prop('init', child.expression, child.expression)); + external_statements.push(...init); + }); + } else if (child.type === 'ConstTag') { /** @type {Statement[]} */ const init = []; context.visit(child, { ...context.state, init }); - props.properties.push(b.prop('init', child.expression, child.expression)); - snippet_statements.push(...init); + external_statements.push(...init); } else { nodes.push(child); } } + snippets_visits.forEach((visit) => visit()); + const block = /** @type {BlockStatement} */ (context.visit({ ...node.fragment, nodes })); const boundary = b.stmt( @@ -56,6 +69,6 @@ export function SvelteBoundary(node, context) { context.state.template.push(''); context.state.init.push( - snippet_statements.length > 0 ? b.block([...snippet_statements, boundary]) : boundary + external_statements.length > 0 ? b.block([...external_statements, boundary]) : boundary ); } diff --git a/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/FlakyComponent.svelte b/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/FlakyComponent.svelte new file mode 100644 index 0000000000..8bbec90de4 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/FlakyComponent.svelte @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/_config.js b/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/_config.js new file mode 100644 index 0000000000..4338969a48 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/_config.js @@ -0,0 +1,17 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + html: '

2

', + mode: ['client'], + test({ target, assert }) { + const btn = target.querySelector('button'); + const p = target.querySelector('p'); + + flushSync(() => { + btn?.click(); + }); + + assert.equal(p?.innerHTML, '4'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/main.svelte b/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/main.svelte new file mode 100644 index 0000000000..25ea8a3ffc --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/main.svelte @@ -0,0 +1,14 @@ + + + + + + {@const double = test * 2} + {#snippet failed()} +

{double}

+ {/snippet} + +
\ No newline at end of file diff --git a/packages/svelte/tests/validator/samples/const-tag-placement-svelte-boundary/errors.json b/packages/svelte/tests/validator/samples/const-tag-placement-svelte-boundary/errors.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/packages/svelte/tests/validator/samples/const-tag-placement-svelte-boundary/errors.json @@ -0,0 +1 @@ +[] diff --git a/packages/svelte/tests/validator/samples/const-tag-placement-svelte-boundary/input.svelte b/packages/svelte/tests/validator/samples/const-tag-placement-svelte-boundary/input.svelte new file mode 100644 index 0000000000..5708cc36ca --- /dev/null +++ b/packages/svelte/tests/validator/samples/const-tag-placement-svelte-boundary/input.svelte @@ -0,0 +1,11 @@ + + + + {@const x = a} + {#snippet failed()} + {x} + {/snippet} + + \ No newline at end of file From 99fdc3f0ab2043af1c6cfc23f6aa886d047ddb0d Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Tue, 14 Jan 2025 10:51:57 +0100 Subject: [PATCH 006/280] docs: update error message for `const_tag_invalid_placement` (#15003) --- documentation/docs/98-reference/.generated/compile-errors.md | 2 +- packages/svelte/messages/compile-errors/template.md | 2 +- packages/svelte/src/compiler/errors.js | 4 ++-- .../tests/validator/samples/const-tag-placement-1/errors.json | 2 +- .../tests/validator/samples/const-tag-placement-2/errors.json | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/documentation/docs/98-reference/.generated/compile-errors.md b/documentation/docs/98-reference/.generated/compile-errors.md index a867cfe88c..2fef3bd45d 100644 --- a/documentation/docs/98-reference/.generated/compile-errors.md +++ b/documentation/docs/98-reference/.generated/compile-errors.md @@ -187,7 +187,7 @@ Cyclical dependency detected: %cycle% ### const_tag_invalid_placement ``` -`{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `` or `` +`{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, ``, `` ``` ### constant_assignment diff --git a/packages/svelte/messages/compile-errors/template.md b/packages/svelte/messages/compile-errors/template.md index 287178ef87..d061416dbd 100644 --- a/packages/svelte/messages/compile-errors/template.md +++ b/packages/svelte/messages/compile-errors/template.md @@ -118,7 +118,7 @@ ## const_tag_invalid_placement -> `{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `` or `` +> `{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, ``, `` ## debug_tag_invalid_arguments diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js index da038af430..53a6ac6849 100644 --- a/packages/svelte/src/compiler/errors.js +++ b/packages/svelte/src/compiler/errors.js @@ -888,12 +888,12 @@ export function const_tag_invalid_expression(node) { } /** - * `{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `` or `` + * `{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, ``, `` * @param {null | number | NodeLike} node * @returns {never} */ export function const_tag_invalid_placement(node) { - e(node, 'const_tag_invalid_placement', `\`{@const}\` must be the immediate child of \`{#snippet}\`, \`{#if}\`, \`{:else if}\`, \`{:else}\`, \`{#each}\`, \`{:then}\`, \`{:catch}\`, \`\` or \`\`\nhttps://svelte.dev/e/const_tag_invalid_placement`); + e(node, 'const_tag_invalid_placement', `\`{@const}\` must be the immediate child of \`{#snippet}\`, \`{#if}\`, \`{:else if}\`, \`{:else}\`, \`{#each}\`, \`{:then}\`, \`{:catch}\`, \`\`, \`\`\nhttps://svelte.dev/e/const_tag_invalid_placement`); } /** diff --git a/packages/svelte/tests/validator/samples/const-tag-placement-1/errors.json b/packages/svelte/tests/validator/samples/const-tag-placement-1/errors.json index 144345527a..514e5d0561 100644 --- a/packages/svelte/tests/validator/samples/const-tag-placement-1/errors.json +++ b/packages/svelte/tests/validator/samples/const-tag-placement-1/errors.json @@ -1,7 +1,7 @@ [ { "code": "const_tag_invalid_placement", - "message": "`{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `` or ``", + "message": "`{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, ``, ``", "start": { "line": 5, "column": 0 diff --git a/packages/svelte/tests/validator/samples/const-tag-placement-2/errors.json b/packages/svelte/tests/validator/samples/const-tag-placement-2/errors.json index 64a24c5f48..6b968f7eda 100644 --- a/packages/svelte/tests/validator/samples/const-tag-placement-2/errors.json +++ b/packages/svelte/tests/validator/samples/const-tag-placement-2/errors.json @@ -1,7 +1,7 @@ [ { "code": "const_tag_invalid_placement", - "message": "`{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `` or ``", + "message": "`{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, ``, ``", "start": { "line": 7, "column": 4 From dae4c5f5e191927d3f05a156b986b605b6d1e97a Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Tue, 14 Jan 2025 15:58:42 +0000 Subject: [PATCH 007/280] fix: ensure signal write invalidation within effects is consistent (#14989) * fix: ensure signal write invalidation within effects is persistent * fix: ensure signal write invalidation within effects is persistent * fix: ensure signal write invalidation within effects is persistent * address feedback --- .changeset/tricky-spiders-collect.md | 5 ++ .../src/internal/client/reactivity/sources.js | 22 +++----- .../svelte/src/internal/client/runtime.js | 55 +++++++++++++++---- packages/svelte/tests/signals/test.ts | 36 ++++++++++++ 4 files changed, 93 insertions(+), 25 deletions(-) create mode 100644 .changeset/tricky-spiders-collect.md diff --git a/.changeset/tricky-spiders-collect.md b/.changeset/tricky-spiders-collect.md new file mode 100644 index 0000000000..0ea8b71e5e --- /dev/null +++ b/.changeset/tricky-spiders-collect.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure signal write invalidation within effects is consistent diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index e0facf3b55..4500a7c5a8 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -3,7 +3,6 @@ import { DEV } from 'esm-env'; import { component_context, active_reaction, - new_deps, active_effect, untracked_writes, get, @@ -29,7 +28,8 @@ import { INSPECT_EFFECT, UNOWNED, MAYBE_DIRTY, - BLOCK_EFFECT + BLOCK_EFFECT, + ROOT_EFFECT } from '../constants.js'; import * as e from '../errors.js'; import { legacy_mode_flag, tracing_mode_flag } from '../../flags/index.js'; @@ -182,26 +182,20 @@ export function internal_set(source, value) { mark_reactions(source, DIRTY); - // If the current signal is running for the first time, it won't have any - // reactions as we only allocate and assign the reactions after the signal - // has fully executed. So in the case of ensuring it registers the reaction + // It's possible that the current reaction might not have up-to-date dependencies + // whilst it's actively running. So in the case of ensuring it registers the reaction // properly for itself, we need to ensure the current effect actually gets // scheduled. i.e: `$effect(() => x++)` if ( is_runes() && active_effect !== null && (active_effect.f & CLEAN) !== 0 && - (active_effect.f & BRANCH_EFFECT) === 0 + (active_effect.f & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ) { - if (new_deps !== null && new_deps.includes(source)) { - set_signal_status(active_effect, DIRTY); - schedule_effect(active_effect); + if (untracked_writes === null) { + set_untracked_writes([source]); } else { - if (untracked_writes === null) { - set_untracked_writes([source]); - } else { - untracked_writes.push(source); - } + untracked_writes.push(source); } } diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 3c8879eb31..eca5ee94f9 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -382,6 +382,34 @@ export function handle_error(error, effect, previous_effect, component_context) } } +/** + * @param {Value} signal + * @param {Effect} effect + * @param {number} [depth] + */ +function schedule_possible_effect_self_invalidation(signal, effect, depth = 0) { + var reactions = signal.reactions; + if (reactions === null) return; + + for (var i = 0; i < reactions.length; i++) { + var reaction = reactions[i]; + if ((reaction.f & DERIVED) !== 0) { + schedule_possible_effect_self_invalidation( + /** @type {Derived} */ (reaction), + effect, + depth + 1 + ); + } else if (effect === reaction) { + if (depth === 0) { + set_signal_status(reaction, DIRTY); + } else if ((reaction.f & CLEAN) !== 0) { + set_signal_status(reaction, MAYBE_DIRTY); + } + schedule_effect(/** @type {Effect} */ (reaction)); + } + } +} + /** * @template V * @param {Reaction} reaction @@ -434,6 +462,22 @@ export function update_reaction(reaction) { deps.length = skipped_deps; } + // If we're inside an effect and we have untracked writes, then we need to + // ensure that if any of those untracked writes result in re-invalidation + // of the current effect, then that happens accordingly + if ( + is_runes() && + untracked_writes !== null && + (reaction.f & (DERIVED | MAYBE_DIRTY | DIRTY)) === 0 + ) { + for (i = 0; i < /** @type {Source[]} */ (untracked_writes).length; i++) { + schedule_possible_effect_self_invalidation( + untracked_writes[i], + /** @type {Effect} */ (reaction) + ); + } + } + // If we are returning to an previous reaction then // we need to increment the read version to ensure that // any dependencies in this reaction aren't marked with @@ -907,17 +951,6 @@ export function get(signal) { } else { new_deps.push(signal); } - - if ( - untracked_writes !== null && - active_effect !== null && - (active_effect.f & CLEAN) !== 0 && - (active_effect.f & BRANCH_EFFECT) === 0 && - untracked_writes.includes(signal) - ) { - set_signal_status(active_effect, DIRTY); - schedule_effect(active_effect); - } } } else if (is_derived && /** @type {Derived} */ (signal).deps === null) { var derived = /** @type {Derived} */ (signal); diff --git a/packages/svelte/tests/signals/test.ts b/packages/svelte/tests/signals/test.ts index e198a5a89d..a9d29920cf 100644 --- a/packages/svelte/tests/signals/test.ts +++ b/packages/svelte/tests/signals/test.ts @@ -402,6 +402,42 @@ describe('signals', () => { }; }); + test('schedules rerun when writing to signal before reading it from derived', (runes) => { + if (!runes) return () => {}; + let log: any[] = []; + + const value = state(1); + const double = derived(() => $.get(value) * 2); + + user_effect(() => { + set(value, 10); + log.push($.get(double)); + }); + + return () => { + flushSync(); + assert.deepEqual(log, [20]); + }; + }); + + test('schedules rerun when writing to signal after reading it from derived', (runes) => { + if (!runes) return () => {}; + let log: any[] = []; + + const value = state(1); + const double = derived(() => $.get(value) * 2); + + user_effect(() => { + log.push($.get(double)); + set(value, 10); + }); + + return () => { + flushSync(); + assert.deepEqual(log, [2, 20]); + }; + }); + test('effect teardown is removed on re-run', () => { const count = state(0); let first = true; From a1698c63ec8ffe8974b077154c7a4cff6f791933 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 14 Jan 2025 16:05:05 +0000 Subject: [PATCH 008/280] Version Packages (#15001) Co-authored-by: github-actions[bot] --- .changeset/curvy-lies-rush.md | 5 ----- .changeset/tricky-spiders-collect.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/curvy-lies-rush.md delete mode 100644 .changeset/tricky-spiders-collect.md diff --git a/.changeset/curvy-lies-rush.md b/.changeset/curvy-lies-rush.md deleted file mode 100644 index 25c6ffc1d9..0000000000 --- a/.changeset/curvy-lies-rush.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -feat: allow const tag inside `svelte:boundary` diff --git a/.changeset/tricky-spiders-collect.md b/.changeset/tricky-spiders-collect.md deleted file mode 100644 index 0ea8b71e5e..0000000000 --- a/.changeset/tricky-spiders-collect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure signal write invalidation within effects is consistent diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 4f3b9a168a..e0ae3f313e 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.17.5 + +### Patch Changes + +- feat: allow const tag inside `svelte:boundary` ([#14993](https://github.com/sveltejs/svelte/pull/14993)) + +- fix: ensure signal write invalidation within effects is consistent ([#14989](https://github.com/sveltejs/svelte/pull/14989)) + ## 5.17.4 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index f426b97be4..0b69fb6edf 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.17.4", + "version": "5.17.5", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 69a8833938..b57f60055a 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.17.4'; +export const VERSION = '5.17.5'; export const PUBLIC_VERSION = '5'; From dfa97a5e643655d919a7c6ff80fb3841fe1111f9 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Tue, 14 Jan 2025 17:28:44 +0100 Subject: [PATCH 009/280] feat: allow every children in `template` tags (#15007) * feat: allow every children in `template` tags * Update .changeset/dry-mails-return.md --------- Co-authored-by: Rich Harris --- .changeset/dry-mails-return.md | 5 +++++ packages/svelte/src/html-tree-validation.js | 2 ++ .../samples/invalid-node-placement-template/errors.json | 1 + .../samples/invalid-node-placement-template/input.svelte | 6 ++++++ 4 files changed, 14 insertions(+) create mode 100644 .changeset/dry-mails-return.md create mode 100644 packages/svelte/tests/validator/samples/invalid-node-placement-template/errors.json create mode 100644 packages/svelte/tests/validator/samples/invalid-node-placement-template/input.svelte diff --git a/.changeset/dry-mails-return.md b/.changeset/dry-mails-return.md new file mode 100644 index 0000000000..8f87f8e33b --- /dev/null +++ b/.changeset/dry-mails-return.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +feat: allow `