From 6afb9c6028e19100829d7157c48c168cef32990b Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 10 Jul 2025 09:58:17 -0400 Subject: [PATCH] partial merge --- .changeset/light-rivers-jump.md | 5 - .github/workflows/ecosystem-ci-trigger.yml | 43 ++++--- packages/svelte/CHANGELOG.md | 8 ++ packages/svelte/package.json | 2 +- .../svelte/src/internal/client/context.js | 9 +- .../client/dom/elements/transitions.js | 11 +- packages/svelte/src/internal/client/proxy.js | 16 +-- .../src/internal/client/reactivity/props.js | 8 -- .../src/internal/client/reactivity/sources.js | 8 ++ .../svelte/src/internal/client/runtime.js | 22 ++-- .../svelte/src/internal/client/types.d.ts | 2 - packages/svelte/src/motion/spring.js | 12 +- packages/svelte/src/motion/tweened.js | 6 +- .../src/reactivity/create-subscriber.js | 3 +- packages/svelte/src/reactivity/map.js | 28 +++-- packages/svelte/src/reactivity/set.js | 21 +++- .../src/reactivity/url-search-params.js | 3 +- packages/svelte/src/reactivity/utils.js | 7 -- packages/svelte/src/version.js | 2 +- .../side-effect-derived-map/_config.js | 33 +++++- .../side-effect-derived-map/main.svelte | 106 +++++++++++++++--- .../side-effect-derived-set/_config.js | 12 +- .../side-effect-derived-set/main.svelte | 51 ++++++--- .../side-effect-derived-spring/_config.js | 23 ++++ .../side-effect-derived-spring/main.svelte | 26 +++++ .../side-effect-derived-tween/_config.js | 23 ++++ .../side-effect-derived-tween/main.svelte | 26 +++++ 27 files changed, 386 insertions(+), 130 deletions(-) delete mode 100644 .changeset/light-rivers-jump.md delete mode 100644 packages/svelte/src/reactivity/utils.js create mode 100644 packages/svelte/tests/runtime-runes/samples/side-effect-derived-spring/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/side-effect-derived-spring/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/side-effect-derived-tween/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/side-effect-derived-tween/main.svelte diff --git a/.changeset/light-rivers-jump.md b/.changeset/light-rivers-jump.md deleted file mode 100644 index 2454d57156..0000000000 --- a/.changeset/light-rivers-jump.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: re-evaluate derived props during teardown diff --git a/.github/workflows/ecosystem-ci-trigger.yml b/.github/workflows/ecosystem-ci-trigger.yml index 71df3242e8..7c6b740370 100644 --- a/.github/workflows/ecosystem-ci-trigger.yml +++ b/.github/workflows/ecosystem-ci-trigger.yml @@ -8,9 +8,17 @@ jobs: trigger: runs-on: ubuntu-latest if: github.repository == 'sveltejs/svelte' && github.event.issue.pull_request && startsWith(github.event.comment.body, '/ecosystem-ci run') + permissions: + issues: write # to add / delete reactions + pull-requests: read # to read PR data + actions: read # to check workflow status + contents: read # to clone the repo steps: - - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - - uses: actions/github-script@v6 + - name: monitor action permissions + uses: GitHubSecurityLab/actions-permissions/monitor@v1 + - name: check user authorization # user needs triage permission + uses: actions/github-script@v7 + id: check-permissions with: script: | const user = context.payload.sender.login @@ -29,7 +37,7 @@ jobs: } if (hasTriagePermission) { - console.log('Allowed') + console.log('User is allowed. Adding +1 reaction.') await github.rest.reactions.createForIssueComment({ owner: context.repo.owner, repo: context.repo.repo, @@ -37,16 +45,18 @@ jobs: content: '+1', }) } else { - console.log('Not allowed') + console.log('User is not allowed. Adding -1 reaction.') await github.rest.reactions.createForIssueComment({ owner: context.repo.owner, repo: context.repo.repo, comment_id: context.payload.comment.id, content: '-1', }) - throw new Error('not allowed') + throw new Error('User does not have the necessary permissions.') } - - uses: actions/github-script@v6 + + - name: Get PR Data + uses: actions/github-script@v7 id: get-pr-data with: script: | @@ -59,21 +69,27 @@ jobs: return { num: context.issue.number, branchName: pr.head.ref, + commit: pr.head.sha, repo: pr.head.repo.full_name } - - id: generate-token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 #keep pinned for security reasons, currently 1.8.0 + + - name: Generate Token + id: generate-token + uses: actions/create-github-app-token@v2 with: - app_id: ${{ secrets.ECOSYSTEM_CI_GITHUB_APP_ID }} - private_key: ${{ secrets.ECOSYSTEM_CI_GITHUB_APP_PRIVATE_KEY }} - repository: '${{ github.repository_owner }}/svelte-ecosystem-ci' - - uses: actions/github-script@v6 + app-id: ${{ secrets.ECOSYSTEM_CI_GITHUB_APP_ID }} + private-key: ${{ secrets.ECOSYSTEM_CI_GITHUB_APP_PRIVATE_KEY }} + repositories: | + svelte + svelte-ecosystem-ci + + - name: Trigger Downstream Workflow + uses: actions/github-script@v7 id: trigger env: COMMENT: ${{ github.event.comment.body }} with: github-token: ${{ steps.generate-token.outputs.token }} - result-encoding: string script: | const comment = process.env.COMMENT.trim() const prData = ${{ steps.get-pr-data.outputs.result }} @@ -89,6 +105,7 @@ jobs: prNumber: '' + prData.num, branchName: prData.branchName, repo: prData.repo, + commit: prData.commit, suite: suite === '' ? '-' : suite } }) diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 89ae380840..19aa1466c0 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.35.5 + +### Patch Changes + +- fix: associate sources in Spring/Tween/SvelteMap/SvelteSet with correct reaction ([#16325](https://github.com/sveltejs/svelte/pull/16325)) + +- fix: re-evaluate derived props during teardown ([#16278](https://github.com/sveltejs/svelte/pull/16278)) + ## 5.35.4 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 7c4fd88617..378fad72ca 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.35.4", + "version": "5.35.5", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/internal/client/context.js b/packages/svelte/src/internal/client/context.js index 93c14e6c05..18d906890d 100644 --- a/packages/svelte/src/internal/client/context.js +++ b/packages/svelte/src/internal/client/context.js @@ -146,20 +146,15 @@ export function getAllContexts() { * @returns {void} */ export function push(props, runes = false, fn) { - var ctx = (component_context = { + component_context = { p: component_context, c: null, - d: false, e: null, m: false, s: props, x: null, l: legacy_mode_flag && !runes ? { s: null, u: null, $: [] } : null - }); - - teardown(() => { - /** @type {ComponentContext} */ (ctx).d = true; - }); + }; if (DEV) { // component function diff --git a/packages/svelte/src/internal/client/dom/elements/transitions.js b/packages/svelte/src/internal/client/dom/elements/transitions.js index 38100e982c..00fad9ffdb 100644 --- a/packages/svelte/src/internal/client/dom/elements/transitions.js +++ b/packages/svelte/src/internal/client/dom/elements/transitions.js @@ -209,21 +209,14 @@ export function transition(flags, element, get_fn, get_params) { var outro; function get_options() { - var previous_reaction = active_reaction; - var previous_effect = active_effect; - set_active_reaction(null); - set_active_effect(null); - try { + return without_reactive_context(() => { // If a transition is still ongoing, we use the existing options rather than generating // new ones. This ensures that reversible transitions reverse smoothly, rather than // jumping to a new spot because (for example) a different `duration` was used return (current_options ??= get_fn()(element, get_params?.() ?? /** @type {P} */ ({}), { direction })); - } finally { - set_active_reaction(previous_reaction); - set_active_effect(previous_effect); - } + }); } /** @type {TransitionManager} */ diff --git a/packages/svelte/src/internal/client/proxy.js b/packages/svelte/src/internal/client/proxy.js index d9063aee34..97c8da9d33 100644 --- a/packages/svelte/src/internal/client/proxy.js +++ b/packages/svelte/src/internal/client/proxy.js @@ -8,7 +8,7 @@ import { is_array, object_prototype } from '../shared/utils.js'; -import { state as source, set } from './reactivity/sources.js'; +import { state as source, set, increment } from './reactivity/sources.js'; import { PROXY_PATH_SYMBOL, STATE_SYMBOL } from '#client/constants'; import { UNINITIALIZED } from '../../constants.js'; import * as e from './errors.js'; @@ -118,7 +118,7 @@ export function proxy(value) { if (prop in target) { const s = with_parent(() => source(UNINITIALIZED, stack)); sources.set(prop, s); - update_version(version); + increment(version); if (DEV) { tag(s, get_label(path, prop)); @@ -136,7 +136,7 @@ export function proxy(value) { } } set(s, UNINITIALIZED); - update_version(version); + increment(version); } return true; @@ -304,7 +304,7 @@ export function proxy(value) { } } - update_version(version); + increment(version); } return true; @@ -343,14 +343,6 @@ function get_label(path, prop) { return /^\d+$/.test(prop) ? `${path}[${prop}]` : `${path}['${prop}']`; } -/** - * @param {Source} signal - * @param {1 | -1} [d] - */ -function update_version(signal, d = 1) { - set(signal, signal.v + d); -} - /** * @param {any} value */ diff --git a/packages/svelte/src/internal/client/reactivity/props.js b/packages/svelte/src/internal/client/reactivity/props.js index 03daad5251..f39d45bb04 100644 --- a/packages/svelte/src/internal/client/reactivity/props.js +++ b/packages/svelte/src/internal/client/reactivity/props.js @@ -268,14 +268,6 @@ export function spread_props(...props) { return new Proxy({ props }, spread_props_handler); } -/** - * @param {Derived} current_value - * @returns {boolean} - */ -function has_destroyed_component_ctx(current_value) { - return current_value.ctx?.d ?? false; -} - /** * This function is responsible for synchronizing a possibly bound prop with the inner component state. * It is used whenever the compiler sees that the component writes to the prop, or when it has a default prop_value. diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index 75a65b1678..4185fa1954 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -264,6 +264,14 @@ export function update_pre(source, d = 1) { return set(source, d === 1 ? ++value : --value); } +/** + * Silently (without using `get`) increment a source + * @param {Source} source + */ +export function increment(source) { + set(source, source.v + 1); +} + /** * @param {Value} signal * @param {number} status should be DIRTY or MAYBE_DIRTY diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index e10115c6fe..d62df4fb5b 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -133,6 +133,8 @@ export let write_version = 1; /** @type {number} Used to version each read of a source of derived to avoid duplicating depedencies inside a reaction */ let read_version = 0; +export let update_version = read_version; + // If we are working with a get() chain that has no active container, // to prevent memory leaks, we skip adding the reaction. export let skip_reaction = false; @@ -237,17 +239,17 @@ function schedule_possible_effect_self_invalidation(signal, effect, root = true) var reactions = signal.reactions; if (reactions === null) return; + if ( + !async_mode_flag && + source_ownership?.reaction === active_reaction && + source_ownership.sources.includes(signal) + ) { + return; + } + for (var i = 0; i < reactions.length; i++) { var reaction = reactions[i]; - if ( - !async_mode_flag && - source_ownership?.reaction === active_reaction && - source_ownership.sources.includes(signal) - ) { - continue; - } - if ((reaction.f & DERIVED) !== 0) { schedule_possible_effect_self_invalidation(/** @type {Derived} */ (reaction), effect, false); } else if (effect === reaction) { @@ -271,6 +273,7 @@ export function update_reaction(reaction) { var previous_reaction_sources = source_ownership; var previous_component_context = component_context; var previous_untracking = untracking; + var previous_update_version = update_version; var flags = reaction.f; @@ -284,7 +287,7 @@ export function update_reaction(reaction) { source_ownership = null; set_component_context(reaction.ctx); untracking = false; - read_version++; + update_version = ++read_version; if (reaction.ac !== null) { reaction.ac.abort(STALE_REACTION); @@ -376,6 +379,7 @@ export function update_reaction(reaction) { source_ownership = previous_reaction_sources; set_component_context(previous_component_context); untracking = previous_untracking; + update_version = previous_update_version; } } diff --git a/packages/svelte/src/internal/client/types.d.ts b/packages/svelte/src/internal/client/types.d.ts index eb386ce583..916169e9ff 100644 --- a/packages/svelte/src/internal/client/types.d.ts +++ b/packages/svelte/src/internal/client/types.d.ts @@ -14,8 +14,6 @@ export type ComponentContext = { p: null | ComponentContext; /** context */ c: null | Map; - /** destroyed */ - d: boolean; /** deferred effects */ e: null | Array<{ fn: () => void | (() => void); diff --git a/packages/svelte/src/motion/spring.js b/packages/svelte/src/motion/spring.js index 0f3bc6fb9f..44be1a501b 100644 --- a/packages/svelte/src/motion/spring.js +++ b/packages/svelte/src/motion/spring.js @@ -5,7 +5,7 @@ import { writable } from '../store/shared/index.js'; import { loop } from '../internal/client/loop.js'; import { raf } from '../internal/client/timing.js'; import { is_date } from './utils.js'; -import { set, source } from '../internal/client/reactivity/sources.js'; +import { set, state } from '../internal/client/reactivity/sources.js'; import { render_effect } from '../internal/client/reactivity/effects.js'; import { tag } from '../internal/client/dev/tracing.js'; import { get } from '../internal/client/runtime.js'; @@ -170,9 +170,9 @@ export function spring(value, opts = {}) { * @since 5.8.0 */ export class Spring { - #stiffness = source(0.15); - #damping = source(0.8); - #precision = source(0.01); + #stiffness = state(0.15); + #damping = state(0.8); + #precision = state(0.01); #current; #target; @@ -194,8 +194,8 @@ export class Spring { * @param {SpringOpts} [options] */ constructor(value, options = {}) { - this.#current = DEV ? tag(source(value), 'Spring.current') : source(value); - this.#target = DEV ? tag(source(value), 'Spring.target') : source(value); + this.#current = DEV ? tag(state(value), 'Spring.current') : state(value); + this.#target = DEV ? tag(state(value), 'Spring.target') : state(value); if (typeof options.stiffness === 'number') this.#stiffness.v = clamp(options.stiffness, 0, 1); if (typeof options.damping === 'number') this.#damping.v = clamp(options.damping, 0, 1); diff --git a/packages/svelte/src/motion/tweened.js b/packages/svelte/src/motion/tweened.js index 09bd06c325..437c22ec3b 100644 --- a/packages/svelte/src/motion/tweened.js +++ b/packages/svelte/src/motion/tweened.js @@ -6,7 +6,7 @@ import { raf } from '../internal/client/timing.js'; import { loop } from '../internal/client/loop.js'; import { linear } from '../easing/index.js'; import { is_date } from './utils.js'; -import { set, source } from '../internal/client/reactivity/sources.js'; +import { set, state } from '../internal/client/reactivity/sources.js'; import { tag } from '../internal/client/dev/tracing.js'; import { get, render_effect } from 'svelte/internal/client'; import { DEV } from 'esm-env'; @@ -191,8 +191,8 @@ export class Tween { * @param {TweenedOptions} options */ constructor(value, options = {}) { - this.#current = source(value); - this.#target = source(value); + this.#current = state(value); + this.#target = state(value); this.#defaults = options; if (DEV) { diff --git a/packages/svelte/src/reactivity/create-subscriber.js b/packages/svelte/src/reactivity/create-subscriber.js index df36064e96..dcbc5df9fe 100644 --- a/packages/svelte/src/reactivity/create-subscriber.js +++ b/packages/svelte/src/reactivity/create-subscriber.js @@ -1,8 +1,7 @@ import { get, tick, untrack } from '../internal/client/runtime.js'; import { effect_tracking, render_effect } from '../internal/client/reactivity/effects.js'; -import { source } from '../internal/client/reactivity/sources.js'; +import { source, increment } from '../internal/client/reactivity/sources.js'; import { tag } from '../internal/client/dev/tracing.js'; -import { increment } from './utils.js'; import { DEV } from 'esm-env'; /** diff --git a/packages/svelte/src/reactivity/map.js b/packages/svelte/src/reactivity/map.js index cd2fac163f..014b5e7c7c 100644 --- a/packages/svelte/src/reactivity/map.js +++ b/packages/svelte/src/reactivity/map.js @@ -1,9 +1,8 @@ /** @import { Source } from '#client' */ import { DEV } from 'esm-env'; -import { set, source, state } from '../internal/client/reactivity/sources.js'; +import { set, source, state, increment } from '../internal/client/reactivity/sources.js'; import { label, tag } from '../internal/client/dev/tracing.js'; -import { get } from '../internal/client/runtime.js'; -import { increment } from './utils.js'; +import { get, update_version } from '../internal/client/runtime.js'; /** * A reactive version of the built-in [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object. @@ -56,6 +55,7 @@ export class SvelteMap extends Map { #sources = new Map(); #version = state(0); #size = state(0); + #update_version = update_version || -1; /** * @param {Iterable | null | undefined} [value] @@ -79,6 +79,19 @@ export class SvelteMap extends Map { } } + /** + * If the source is being created inside the same reaction as the SvelteMap instance, + * we use `state` so that it will not be a dependency of the reaction. Otherwise we + * use `source` so it will be. + * + * @template T + * @param {T} value + * @returns {Source} + */ + #source(value) { + return update_version === this.#update_version ? state(value) : source(value); + } + /** @param {K} key */ has(key) { var sources = this.#sources; @@ -87,7 +100,7 @@ export class SvelteMap extends Map { if (s === undefined) { var ret = super.get(key); if (ret !== undefined) { - s = source(0); + s = this.#source(0); if (DEV) { tag(s, `SvelteMap get(${label(key)})`); @@ -123,7 +136,7 @@ export class SvelteMap extends Map { if (s === undefined) { var ret = super.get(key); if (ret !== undefined) { - s = source(0); + s = this.#source(0); if (DEV) { tag(s, `SvelteMap get(${label(key)})`); @@ -154,7 +167,7 @@ export class SvelteMap extends Map { var version = this.#version; if (s === undefined) { - s = source(0); + s = this.#source(0); if (DEV) { tag(s, `SvelteMap get(${label(key)})`); @@ -219,8 +232,7 @@ export class SvelteMap extends Map { if (this.#size.v !== sources.size) { for (var key of super.keys()) { if (!sources.has(key)) { - var s = source(0); - + var s = this.#source(0); if (DEV) { tag(s, `SvelteMap get(${label(key)})`); } diff --git a/packages/svelte/src/reactivity/set.js b/packages/svelte/src/reactivity/set.js index 8a656c2bc1..d7c2deeaae 100644 --- a/packages/svelte/src/reactivity/set.js +++ b/packages/svelte/src/reactivity/set.js @@ -1,9 +1,8 @@ /** @import { Source } from '#client' */ import { DEV } from 'esm-env'; -import { source, set, state } from '../internal/client/reactivity/sources.js'; +import { source, set, state, increment } from '../internal/client/reactivity/sources.js'; import { label, tag } from '../internal/client/dev/tracing.js'; -import { get } from '../internal/client/runtime.js'; -import { increment } from './utils.js'; +import { get, update_version } from '../internal/client/runtime.js'; var read_methods = ['forEach', 'isDisjointFrom', 'isSubsetOf', 'isSupersetOf']; var set_like_methods = ['difference', 'intersection', 'symmetricDifference', 'union']; @@ -50,6 +49,7 @@ export class SvelteSet extends Set { #sources = new Map(); #version = state(0); #size = state(0); + #update_version = update_version || -1; /** * @param {Iterable | null | undefined} [value] @@ -75,6 +75,19 @@ export class SvelteSet extends Set { if (!inited) this.#init(); } + /** + * If the source is being created inside the same reaction as the SvelteSet instance, + * we use `state` so that it will not be a dependency of the reaction. Otherwise we + * use `source` so it will be. + * + * @template T + * @param {T} value + * @returns {Source} + */ + #source(value) { + return update_version === this.#update_version ? state(value) : source(value); + } + // We init as part of the first instance so that we can treeshake this class #init() { inited = true; @@ -116,7 +129,7 @@ export class SvelteSet extends Set { return false; } - s = source(true); + s = this.#source(true); if (DEV) { tag(s, `SvelteSet has(${label(value)})`); diff --git a/packages/svelte/src/reactivity/url-search-params.js b/packages/svelte/src/reactivity/url-search-params.js index 389da7cdb6..2381e11875 100644 --- a/packages/svelte/src/reactivity/url-search-params.js +++ b/packages/svelte/src/reactivity/url-search-params.js @@ -1,9 +1,8 @@ import { DEV } from 'esm-env'; -import { state } from '../internal/client/reactivity/sources.js'; +import { state, increment } from '../internal/client/reactivity/sources.js'; import { tag } from '../internal/client/dev/tracing.js'; import { get } from '../internal/client/runtime.js'; import { get_current_url } from './url.js'; -import { increment } from './utils.js'; export const REPLACE = Symbol(); diff --git a/packages/svelte/src/reactivity/utils.js b/packages/svelte/src/reactivity/utils.js deleted file mode 100644 index cd55e0e0ba..0000000000 --- a/packages/svelte/src/reactivity/utils.js +++ /dev/null @@ -1,7 +0,0 @@ -/** @import { Source } from '#client' */ -import { set } from '../internal/client/reactivity/sources.js'; - -/** @param {Source} source */ -export function increment(source) { - set(source, source.v + 1); -} diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index c4111b9e8d..eb68753d71 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.35.4'; +export const VERSION = '5.35.5'; export const PUBLIC_VERSION = '5'; diff --git a/packages/svelte/tests/runtime-runes/samples/side-effect-derived-map/_config.js b/packages/svelte/tests/runtime-runes/samples/side-effect-derived-map/_config.js index 5ad6f57e31..c10dc7fb55 100644 --- a/packages/svelte/tests/runtime-runes/samples/side-effect-derived-map/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/side-effect-derived-map/_config.js @@ -8,7 +8,8 @@ export default test({ }, test({ assert, target }) { - const [button1, button2] = target.querySelectorAll('button'); + const [button1, button2, button3, button4, button5, button6, button7, button8] = + target.querySelectorAll('button'); assert.throws(() => { button1?.click(); @@ -19,5 +20,35 @@ export default test({ button2?.click(); flushSync(); }); + + assert.throws(() => { + button3?.click(); + flushSync(); + }, /state_unsafe_mutation/); + + assert.doesNotThrow(() => { + button4?.click(); + flushSync(); + }); + + assert.throws(() => { + button5?.click(); + flushSync(); + }, /state_unsafe_mutation/); + + assert.doesNotThrow(() => { + button6?.click(); + flushSync(); + }); + + assert.throws(() => { + button7?.click(); + flushSync(); + }, /state_unsafe_mutation/); + + assert.doesNotThrow(() => { + button8?.click(); + flushSync(); + }); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/side-effect-derived-map/main.svelte b/packages/svelte/tests/runtime-runes/samples/side-effect-derived-map/main.svelte index bdd5ccb75c..c37f37ceb6 100644 --- a/packages/svelte/tests/runtime-runes/samples/side-effect-derived-map/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/side-effect-derived-map/main.svelte @@ -1,27 +1,101 @@ - -{#if visibleExternal} - {throws} + +{#if outside_basic} + {throw_basic} +{/if} + +{#if inside_basic} + {works_basic} +{/if} + + +{#if outside_has} + {throw_has} {/if} - -{#if visibleInternal} - {works} + +{#if inside_has} + {works_has} {/if} + +{#if outside_get} + {throw_get} +{/if} + +{#if inside_get} + {works_get} +{/if} + + +{#if outside_values} + {throw_values} +{/if} + +{#if inside_values} + {works_values} +{/if} diff --git a/packages/svelte/tests/runtime-runes/samples/side-effect-derived-set/_config.js b/packages/svelte/tests/runtime-runes/samples/side-effect-derived-set/_config.js index 5ad6f57e31..5cf066fb8a 100644 --- a/packages/svelte/tests/runtime-runes/samples/side-effect-derived-set/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/side-effect-derived-set/_config.js @@ -8,7 +8,7 @@ export default test({ }, test({ assert, target }) { - const [button1, button2] = target.querySelectorAll('button'); + const [button1, button2, button3, button4] = target.querySelectorAll('button'); assert.throws(() => { button1?.click(); @@ -19,5 +19,15 @@ export default test({ button2?.click(); flushSync(); }); + + assert.throws(() => { + button3?.click(); + flushSync(); + }, /state_unsafe_mutation/); + + assert.doesNotThrow(() => { + button4?.click(); + flushSync(); + }); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/side-effect-derived-set/main.svelte b/packages/svelte/tests/runtime-runes/samples/side-effect-derived-set/main.svelte index 8564f6e7c4..1d6735ba64 100644 --- a/packages/svelte/tests/runtime-runes/samples/side-effect-derived-set/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/side-effect-derived-set/main.svelte @@ -1,27 +1,52 @@ - -{#if visibleExternal} - {throws} + +{#if outside_basic} + {throws_basic} +{/if} + +{#if inside_basic} + {works_basic} +{/if} + + +{#if outside_has_delete} + {throws_has_delete} {/if} - -{#if visibleInternal} - {works} + +{#if inside_has_delete} + {works_has_delete} {/if} diff --git a/packages/svelte/tests/runtime-runes/samples/side-effect-derived-spring/_config.js b/packages/svelte/tests/runtime-runes/samples/side-effect-derived-spring/_config.js new file mode 100644 index 0000000000..5ad6f57e31 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/side-effect-derived-spring/_config.js @@ -0,0 +1,23 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true, + runes: true + }, + + test({ assert, target }) { + const [button1, button2] = target.querySelectorAll('button'); + + assert.throws(() => { + button1?.click(); + flushSync(); + }, /state_unsafe_mutation/); + + assert.doesNotThrow(() => { + button2?.click(); + flushSync(); + }); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/side-effect-derived-spring/main.svelte b/packages/svelte/tests/runtime-runes/samples/side-effect-derived-spring/main.svelte new file mode 100644 index 0000000000..b0818deca9 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/side-effect-derived-spring/main.svelte @@ -0,0 +1,26 @@ + + + +{#if outside_basic} + {throws_basic} +{/if} + +{#if inside_basic} + {works_basic} +{/if} \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/side-effect-derived-tween/_config.js b/packages/svelte/tests/runtime-runes/samples/side-effect-derived-tween/_config.js new file mode 100644 index 0000000000..5ad6f57e31 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/side-effect-derived-tween/_config.js @@ -0,0 +1,23 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true, + runes: true + }, + + test({ assert, target }) { + const [button1, button2] = target.querySelectorAll('button'); + + assert.throws(() => { + button1?.click(); + flushSync(); + }, /state_unsafe_mutation/); + + assert.doesNotThrow(() => { + button2?.click(); + flushSync(); + }); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/side-effect-derived-tween/main.svelte b/packages/svelte/tests/runtime-runes/samples/side-effect-derived-tween/main.svelte new file mode 100644 index 0000000000..bd007f2b50 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/side-effect-derived-tween/main.svelte @@ -0,0 +1,26 @@ + + + +{#if outside_basic} + {throws_basic} +{/if} + +{#if inside_basic} + {works_basic} +{/if} \ No newline at end of file