diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_avoidable.js b/benchmarking/benchmarks/reactivity/kairo/kairo_avoidable.js index 6b058cdc3c1d..9daea6de99cb 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_avoidable.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_avoidable.js @@ -20,12 +20,12 @@ function setup() { return { destroy, run() { - $.flush_sync(() => { + $.flush(() => { $.set(head, 1); }); assert($.get(computed5) === 6); for (let i = 0; i < 1000; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(head, i); }); assert($.get(computed5) === 6); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_broad.js b/benchmarking/benchmarks/reactivity/kairo/kairo_broad.js index d1cde5958edf..8dc5710c87db 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_broad.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_broad.js @@ -25,12 +25,12 @@ function setup() { return { destroy, run() { - $.flush_sync(() => { + $.flush(() => { $.set(head, 1); }); counter = 0; for (let i = 0; i < 50; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(head, i); }); assert($.get(last) === i + 50); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_deep.js b/benchmarking/benchmarks/reactivity/kairo/kairo_deep.js index 149457ede156..8690c85f864a 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_deep.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_deep.js @@ -25,12 +25,12 @@ function setup() { return { destroy, run() { - $.flush_sync(() => { + $.flush(() => { $.set(head, 1); }); counter = 0; for (let i = 0; i < iter; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(head, i); }); assert($.get(current) === len + i); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_diamond.js b/benchmarking/benchmarks/reactivity/kairo/kairo_diamond.js index 958a1bcd7890..bf4e07ee8962 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_diamond.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_diamond.js @@ -28,13 +28,13 @@ function setup() { return { destroy, run() { - $.flush_sync(() => { + $.flush(() => { $.set(head, 1); }); assert($.get(sum) === 2 * width); counter = 0; for (let i = 0; i < 500; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(head, i); }); assert($.get(sum) === (i + 1) * width); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_mux.js b/benchmarking/benchmarks/reactivity/kairo/kairo_mux.js index b645051c09bf..fc252a27b5f8 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_mux.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_mux.js @@ -22,13 +22,13 @@ function setup() { destroy, run() { for (let i = 0; i < 10; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(heads[i], i); }); assert($.get(splited[i]) === i + 1); } for (let i = 0; i < 10; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(heads[i], i * 2); }); assert($.get(splited[i]) === i * 2 + 1); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_repeated.js b/benchmarking/benchmarks/reactivity/kairo/kairo_repeated.js index 53b85acd3766..3bee06ca0e8f 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_repeated.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_repeated.js @@ -25,13 +25,13 @@ function setup() { return { destroy, run() { - $.flush_sync(() => { + $.flush(() => { $.set(head, 1); }); assert($.get(current) === size); counter = 0; for (let i = 0; i < 100; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(head, i); }); assert($.get(current) === i * size); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_triangle.js b/benchmarking/benchmarks/reactivity/kairo/kairo_triangle.js index b9e2ad9fa4a3..11a419a52e7b 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_triangle.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_triangle.js @@ -38,13 +38,13 @@ function setup() { destroy, run() { const constant = count(width); - $.flush_sync(() => { + $.flush(() => { $.set(head, 1); }); assert($.get(sum) === constant); counter = 0; for (let i = 0; i < 100; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(head, i); }); assert($.get(sum) === constant - width + i * width); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_unstable.js b/benchmarking/benchmarks/reactivity/kairo/kairo_unstable.js index 0e783732dc67..54eb732cb29d 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_unstable.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_unstable.js @@ -25,13 +25,13 @@ function setup() { return { destroy, run() { - $.flush_sync(() => { + $.flush(() => { $.set(head, 1); }); assert($.get(current) === 40); counter = 0; for (let i = 0; i < 100; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(head, i); }); } diff --git a/benchmarking/benchmarks/reactivity/mol_bench.js b/benchmarking/benchmarks/reactivity/mol_bench.js index c9f492f61967..536b078d74a4 100644 --- a/benchmarking/benchmarks/reactivity/mol_bench.js +++ b/benchmarking/benchmarks/reactivity/mol_bench.js @@ -51,11 +51,11 @@ function setup() { */ run(i) { res.length = 0; - $.flush_sync(() => { + $.flush(() => { $.set(B, 1); $.set(A, 1 + i * 2); }); - $.flush_sync(() => { + $.flush(() => { $.set(A, 2 + i * 2); $.set(B, 2); }); diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 5ebb088924b2..907c5a353474 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.20.4 + +### Patch Changes + +- fix: update types and inline docs for flushSync ([#15348](https://github.com/sveltejs/svelte/pull/15348)) + ## 5.20.3 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 5f16c5433012..399d908e7ab4 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.20.3", + "version": "5.20.4", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index ca9297279ee2..322293bf6b91 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -267,11 +267,9 @@ export function analyze_module(ast, options) { expression: null, function_depth: 0, has_props_rune: false, - instance_scope: /** @type {any} */ (null), options: /** @type {ValidatedCompileOptions} */ (options), parent_element: null, - reactive_statement: null, - reactive_statements: new Map() + reactive_statement: null }, visitors ); @@ -566,7 +564,7 @@ export function analyze_component(root, source, options) { binding.declaration_kind !== 'import' ) { binding.kind = 'state'; - binding.mutated = binding.updated = true; + binding.mutated = true; } } } @@ -620,9 +618,7 @@ export function analyze_component(root, source, options) { expression: null, derived_state: [], function_depth: scope.function_depth, - instance_scope: instance.scope, - reactive_statement: null, - reactive_statements: new Map() + reactive_statement: null }; walk(/** @type {AST.SvelteNode} */ (ast), state, visitors); @@ -684,9 +680,7 @@ export function analyze_component(root, source, options) { parent_element: null, has_props_rune: false, ast_type: ast === instance.ast ? 'instance' : ast === template.ast ? 'template' : 'module', - instance_scope: instance.scope, reactive_statement: null, - reactive_statements: analysis.reactive_statements, component_slots: new Set(), expression: null, derived_state: [], diff --git a/packages/svelte/src/compiler/phases/2-analyze/types.d.ts b/packages/svelte/src/compiler/phases/2-analyze/types.d.ts index 70796a0d59b5..17c8123de1fe 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/types.d.ts +++ b/packages/svelte/src/compiler/phases/2-analyze/types.d.ts @@ -1,7 +1,6 @@ import type { Scope } from '../scope.js'; import type { ComponentAnalysis, ReactiveStatement } from '../types.js'; import type { AST, ExpressionMetadata, ValidatedCompileOptions } from '#compiler'; -import type { LabeledStatement } from 'estree'; export interface AnalysisState { scope: Scope; @@ -23,9 +22,7 @@ export interface AnalysisState { function_depth: number; // legacy stuff - instance_scope: Scope; reactive_statement: null | ReactiveStatement; - reactive_statements: Map; } export type Context = import('zimmerframe').Context< diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js index 42e449896928..561a00452684 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js @@ -162,7 +162,7 @@ function get_delegated_event(event_name, handler, context) { return unhoisted; } - if (binding !== null && binding.initial !== null && !binding.updated && !binding.is_called) { + if (binding !== null && binding.initial !== null && !binding.updated) { const binding_type = binding.initial.type; if ( diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/BindDirective.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/BindDirective.js index 7719eee6772e..509fecf301cc 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/BindDirective.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/BindDirective.js @@ -5,7 +5,7 @@ import { is_text_attribute, object } from '../../../utils/ast.js'; -import { validate_no_const_assignment } from './shared/utils.js'; +import { validate_assignment } from './shared/utils.js'; import * as e from '../../../errors.js'; import * as w from '../../../warnings.js'; import { binding_properties } from '../../bindings.js'; @@ -158,7 +158,7 @@ export function BindDirective(node, context) { return; } - validate_no_const_assignment(node, node.expression, context.state.scope, true); + validate_assignment(node, node.expression, context.state); const assignee = node.expression; const left = object(assignee); @@ -184,14 +184,6 @@ export function BindDirective(node, context) { ) { e.bind_invalid_value(node.expression); } - - if (context.state.analysis.runes && binding?.kind === 'each') { - e.each_item_invalid_assignment(node); - } - - if (binding?.kind === 'snippet') { - e.snippet_parameter_assignment(node); - } } if (node.name === 'group') { diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js index ce520cc98055..4d09d9293fb2 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js @@ -213,14 +213,6 @@ export function CallExpression(node, context) { break; } - if (node.callee.type === 'Identifier') { - const binding = context.state.scope.get(node.callee.name); - - if (binding !== null) { - binding.is_called = true; - } - } - // `$inspect(foo)` or `$derived(foo) should not trigger the `static-state-reference` warning if (rune === '$inspect' || rune === '$derived') { context.next({ ...context.state, function_depth: context.state.function_depth + 1 }); diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportSpecifier.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportSpecifier.js index cfb24970de21..2a05ffb92605 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportSpecifier.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportSpecifier.js @@ -22,7 +22,7 @@ export function ExportSpecifier(node, context) { }); const binding = context.state.scope.get(local_name); - if (binding) binding.reassigned = binding.updated = true; + if (binding) binding.reassigned = true; } } else { validate_export(node, context.state.scope, local_name); diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/LabeledStatement.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/LabeledStatement.js index a63480feaabc..514cfae53ce4 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/LabeledStatement.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/LabeledStatement.js @@ -64,7 +64,7 @@ export function LabeledStatement(node, context) { } } - context.state.reactive_statements.set(node, reactive_statement); + context.state.analysis.reactive_statements.set(node, reactive_statement); if ( node.body.type === 'ExpressionStatement' && diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/TaggedTemplateExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/TaggedTemplateExpression.js index eacb8a342ac2..881ee5a85edd 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/TaggedTemplateExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/TaggedTemplateExpression.js @@ -1,4 +1,4 @@ -/** @import { TaggedTemplateExpression, VariableDeclarator } from 'estree' */ +/** @import { TaggedTemplateExpression } from 'estree' */ /** @import { Context } from '../types' */ import { is_pure } from './shared/utils.js'; @@ -12,12 +12,5 @@ export function TaggedTemplateExpression(node, context) { context.state.expression.has_state = true; } - if (node.tag.type === 'Identifier') { - const binding = context.state.scope.get(node.tag.name); - - if (binding !== null) { - binding.is_called = true; - } - } context.next(); } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js index 1507123e1342..04f4347a40bb 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js @@ -10,12 +10,12 @@ import * as b from '../../../../utils/builders.js'; import { get_rune } from '../../../scope.js'; /** - * @param {AssignmentExpression | UpdateExpression} node + * @param {AssignmentExpression | UpdateExpression | AST.BindDirective} node * @param {Pattern | Expression} argument * @param {AnalysisState} state */ export function validate_assignment(node, argument, state) { - validate_no_const_assignment(node, argument, state.scope, false); + validate_no_const_assignment(node, argument, state.scope, node.type === 'BindDirective'); if (argument.type === 'Identifier') { const binding = state.scope.get(argument.name); diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index 2e6307a4b7a6..cf5ba285cbf3 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -312,7 +312,7 @@ export function client_component(analysis, options) { const setter = b.set(key, [ b.stmt(b.call(b.id(name), b.id('$$value'))), - b.stmt(b.call('$.flush_sync')) + b.stmt(b.call('$.flush')) ]); if (analysis.runes && binding.initial) { diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index 41e1c78c057b..f46adf49006e 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -57,10 +57,8 @@ export class Binding { */ metadata = null; - is_called = false; mutated = false; reassigned = false; - updated = false; /** * @@ -77,6 +75,10 @@ export class Binding { this.kind = kind; this.declaration_kind = declaration_kind; } + + get updated() { + return this.mutated || this.reassigned; + } } export class Scope { @@ -738,8 +740,6 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) { const binding = left && scope.get(left.name); if (binding !== null && left !== binding.node) { - binding.updated = true; - if (left === expression) { binding.reassigned = true; } else { diff --git a/packages/svelte/src/index-client.js b/packages/svelte/src/index-client.js index ca29d5bfbe3c..efcf7b727b8d 100644 --- a/packages/svelte/src/index-client.js +++ b/packages/svelte/src/index-client.js @@ -1,7 +1,7 @@ /** @import { ComponentContext, ComponentContextLegacy } from '#client' */ /** @import { EventDispatcher } from './index.js' */ /** @import { NotFunction } from './internal/types.js' */ -import { flush_sync, untrack } from './internal/client/runtime.js'; +import { untrack } from './internal/client/runtime.js'; import { is_array } from './internal/shared/utils.js'; import { user_effect } from './internal/client/index.js'; import * as e from './internal/client/errors.js'; @@ -206,15 +206,7 @@ function init_update_callbacks(context) { return (l.u ??= { a: [], b: [], m: [] }); } -/** - * Synchronously flushes any pending state changes and those that result from it. - * @param {() => void} [fn] - * @returns {void} - */ -export function flushSync(fn) { - flush_sync(fn); -} - +export { flushSync } from './internal/client/runtime.js'; export { getContext, getAllContexts, hasContext, setContext } from './internal/client/context.js'; export { hydrate, mount, unmount } from './internal/client/render.js'; export { tick, untrack } from './internal/client/runtime.js'; diff --git a/packages/svelte/src/internal/client/dom/blocks/await.js b/packages/svelte/src/internal/client/dom/blocks/await.js index c8c7c1c0ea77..2e3d22977914 100644 --- a/packages/svelte/src/internal/client/dom/blocks/await.js +++ b/packages/svelte/src/internal/client/dom/blocks/await.js @@ -3,7 +3,7 @@ import { DEV } from 'esm-env'; import { is_promise } from '../../../shared/utils.js'; import { block, branch, pause_effect, resume_effect } from '../../reactivity/effects.js'; import { internal_set, mutable_source, source } from '../../reactivity/sources.js'; -import { flush_sync, set_active_effect, set_active_reaction } from '../../runtime.js'; +import { flushSync, set_active_effect, set_active_reaction } from '../../runtime.js'; import { hydrate_next, hydrate_node, hydrating } from '../hydration.js'; import { queue_micro_task } from '../task.js'; import { UNINITIALIZED } from '../../../../constants.js'; @@ -105,7 +105,7 @@ export function await_block(node, get_input, pending_fn, then_fn, catch_fn) { // without this, the DOM does not update until two ticks after the promise // resolves, which is unexpected behaviour (and somewhat irksome to test) - flush_sync(); + flushSync(); } } } diff --git a/packages/svelte/src/internal/client/dom/task.js b/packages/svelte/src/internal/client/dom/task.js index acb5a5b117f0..48a2fbe660eb 100644 --- a/packages/svelte/src/internal/client/dom/task.js +++ b/packages/svelte/src/internal/client/dom/task.js @@ -1,30 +1,26 @@ import { run_all } from '../../shared/utils.js'; // Fallback for when requestIdleCallback is not available -export const request_idle_callback = +const request_idle_callback = typeof requestIdleCallback === 'undefined' ? (/** @type {() => void} */ cb) => setTimeout(cb, 1) : requestIdleCallback; -let is_micro_task_queued = false; -let is_idle_task_queued = false; - /** @type {Array<() => void>} */ -let current_queued_micro_tasks = []; +let micro_tasks = []; + /** @type {Array<() => void>} */ -let current_queued_idle_tasks = []; +let idle_tasks = []; -function process_micro_tasks() { - is_micro_task_queued = false; - const tasks = current_queued_micro_tasks.slice(); - current_queued_micro_tasks = []; +function run_micro_tasks() { + var tasks = micro_tasks; + micro_tasks = []; run_all(tasks); } -function process_idle_tasks() { - is_idle_task_queued = false; - const tasks = current_queued_idle_tasks.slice(); - current_queued_idle_tasks = []; +function run_idle_tasks() { + var tasks = idle_tasks; + idle_tasks = []; run_all(tasks); } @@ -32,32 +28,33 @@ function process_idle_tasks() { * @param {() => void} fn */ export function queue_micro_task(fn) { - if (!is_micro_task_queued) { - is_micro_task_queued = true; - queueMicrotask(process_micro_tasks); + if (micro_tasks.length === 0) { + queueMicrotask(run_micro_tasks); } - current_queued_micro_tasks.push(fn); + + micro_tasks.push(fn); } /** * @param {() => void} fn */ export function queue_idle_task(fn) { - if (!is_idle_task_queued) { - is_idle_task_queued = true; - request_idle_callback(process_idle_tasks); + if (idle_tasks.length === 0) { + request_idle_callback(run_idle_tasks); } - current_queued_idle_tasks.push(fn); + + idle_tasks.push(fn); } /** * Synchronously run any queued tasks. */ export function flush_tasks() { - if (is_micro_task_queued) { - process_micro_tasks(); + if (micro_tasks.length > 0) { + run_micro_tasks(); } - if (is_idle_task_queued) { - process_idle_tasks(); + + if (idle_tasks.length > 0) { + run_idle_tasks(); } } diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 431ac8cf2492..31da00dbb448 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -139,7 +139,7 @@ export { get, safe_get, invalidate_inner_signals, - flush_sync, + flushSync as flush, tick, untrack, exclude_from_object, diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 59a7ed0f16d6..795417cc0fdb 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -116,7 +116,7 @@ function get_derived_parent_effect(derived) { * @param {Derived} derived * @returns {T} */ -export function execute_derived(derived) { +function execute_derived(derived) { var value; var prev_active_effect = active_effect; diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 9d7b5e9de624..28589ce94df1 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -6,15 +6,12 @@ import { update_effect, get, is_destroying_effect, - is_flushing_effect, remove_reactions, schedule_effect, set_active_reaction, set_is_destroying_effect, - set_is_flushing_effect, set_signal_status, untrack, - skip_reaction, untracking } from '../runtime.js'; import { @@ -118,17 +115,12 @@ function create_effect(type, fn, sync, push = true) { } if (sync) { - var previously_flushing_effect = is_flushing_effect; - try { - set_is_flushing_effect(true); update_effect(effect); effect.f |= EFFECT_RAN; } catch (e) { destroy_effect(effect); throw e; - } finally { - set_is_flushing_effect(previously_flushing_effect); } } else if (fn !== null) { schedule_effect(effect); diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index ded0ca05846b..f6a3fd7e330a 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -14,8 +14,6 @@ import { derived_sources, set_derived_sources, check_dirtiness, - set_is_flushing_effect, - is_flushing_effect, untracking } from '../runtime.js'; import { equals, safe_equals } from './equality.js'; @@ -202,22 +200,18 @@ export function internal_set(source, value) { if (DEV && inspect_effects.size > 0) { const inspects = Array.from(inspect_effects); - var previously_flushing_effect = is_flushing_effect; - set_is_flushing_effect(true); - try { - for (const effect of inspects) { - // Mark clean inspect-effects as maybe dirty and then check their dirtiness - // instead of just updating the effects - this way we avoid overfiring. - if ((effect.f & CLEAN) !== 0) { - set_signal_status(effect, MAYBE_DIRTY); - } - if (check_dirtiness(effect)) { - update_effect(effect); - } + + for (const effect of inspects) { + // Mark clean inspect-effects as maybe dirty and then check their dirtiness + // instead of just updating the effects - this way we avoid overfiring. + if ((effect.f & CLEAN) !== 0) { + set_signal_status(effect, MAYBE_DIRTY); + } + if (check_dirtiness(effect)) { + update_effect(effect); } - } finally { - set_is_flushing_effect(previously_flushing_effect); } + inspect_effects.clear(); } } diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 8c98f948f3f0..fe4104c1081d 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -9,7 +9,6 @@ import { } from './reactivity/effects.js'; import { EFFECT, - RENDER_EFFECT, DIRTY, MAYBE_DIRTY, CLEAN, @@ -41,28 +40,19 @@ import { } from './context.js'; import { is_firefox } from './dom/operations.js'; -const FLUSH_MICROTASK = 0; -const FLUSH_SYNC = 1; // Used for DEV time error handling /** @param {WeakSet} value */ const handled_errors = new WeakSet(); -export let is_throwing_error = false; +let is_throwing_error = false; -// Used for controlling the flush of effects. -let scheduler_mode = FLUSH_MICROTASK; -// Used for handling scheduling -let is_micro_task_queued = false; +let is_flushing = false; /** @type {Effect | null} */ let last_scheduled_effect = null; -export let is_flushing_effect = false; -export let is_destroying_effect = false; +let is_updating_effect = false; -/** @param {boolean} value */ -export function set_is_flushing_effect(value) { - is_flushing_effect = value; -} +export let is_destroying_effect = false; /** @param {boolean} value */ export function set_is_destroying_effect(value) { @@ -74,7 +64,6 @@ export function set_is_destroying_effect(value) { /** @type {Effect[]} */ let queued_root_effects = []; -let flush_count = 0; /** @type {Effect[]} Stack of effects, dev only */ let dev_effect_stack = []; // Handle signal reactivity tree dependencies and reactions @@ -117,7 +106,7 @@ export function set_derived_sources(sources) { * and until a new dependency is accessed — we track this via `skipped_deps` * @type {null | Value[]} */ -export let new_deps = null; +let new_deps = null; let skipped_deps = 0; @@ -410,10 +399,9 @@ export function update_reaction(reaction) { new_deps = /** @type {null | Value[]} */ (null); skipped_deps = 0; untracked_writes = null; - active_reaction = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null; skip_reaction = - (flags & UNOWNED) !== 0 && - (!is_flushing_effect || previous_reaction === null || previous_untracking); + (flags & UNOWNED) !== 0 && (untracking || !is_updating_effect || active_reaction === null); + active_reaction = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null; derived_sources = null; set_component_context(reaction.ctx); @@ -559,8 +547,10 @@ export function update_effect(effect) { var previous_effect = active_effect; var previous_component_context = component_context; + var was_updating_effect = is_updating_effect; active_effect = effect; + is_updating_effect = true; if (DEV) { var previous_component_fn = dev_current_component_function; @@ -602,6 +592,7 @@ export function update_effect(effect) { } catch (error) { handle_error(error, effect, previous_effect, previous_component_context || effect.ctx); } finally { + is_updating_effect = was_updating_effect; active_effect = previous_effect; if (DEV) { @@ -620,69 +611,70 @@ function log_effect_stack() { } function infinite_loop_guard() { - if (flush_count > 1000) { - flush_count = 0; - try { - e.effect_update_depth_exceeded(); - } catch (error) { + try { + e.effect_update_depth_exceeded(); + } catch (error) { + if (DEV) { + // stack is garbage, ignore. Instead add a console.error message. + define_property(error, 'stack', { + value: '' + }); + } + // Try and handle the error so it can be caught at a boundary, that's + // if there's an effect available from when it was last scheduled + if (last_scheduled_effect !== null) { if (DEV) { - // stack is garbage, ignore. Instead add a console.error message. - define_property(error, 'stack', { - value: '' - }); - } - // Try and handle the error so it can be caught at a boundary, that's - // if there's an effect available from when it was last scheduled - if (last_scheduled_effect !== null) { - if (DEV) { - try { - handle_error(error, last_scheduled_effect, null, null); - } catch (e) { - // Only log the effect stack if the error is re-thrown - log_effect_stack(); - throw e; - } - } else { + try { handle_error(error, last_scheduled_effect, null, null); - } - } else { - if (DEV) { + } catch (e) { + // Only log the effect stack if the error is re-thrown log_effect_stack(); + throw e; } - throw error; + } else { + handle_error(error, last_scheduled_effect, null, null); } + } else { + if (DEV) { + log_effect_stack(); + } + throw error; } } - flush_count++; } -/** - * @param {Array} root_effects - * @returns {void} - */ -function flush_queued_root_effects(root_effects) { - var length = root_effects.length; - if (length === 0) { - return; - } - infinite_loop_guard(); - - var previously_flushing_effect = is_flushing_effect; - is_flushing_effect = true; - +function flush_queued_root_effects() { try { - for (var i = 0; i < length; i++) { - var effect = root_effects[i]; + var flush_count = 0; - if ((effect.f & CLEAN) === 0) { - effect.f ^= CLEAN; + while (queued_root_effects.length > 0) { + if (flush_count++ > 1000) { + infinite_loop_guard(); } - var collected_effects = process_effects(effect); - flush_queued_effects(collected_effects); + var root_effects = queued_root_effects; + var length = root_effects.length; + + queued_root_effects = []; + + for (var i = 0; i < length; i++) { + var root = root_effects[i]; + + if ((root.f & CLEAN) === 0) { + root.f ^= CLEAN; + } + + var collected_effects = process_effects(root); + flush_queued_effects(collected_effects); + } } } finally { - is_flushing_effect = previously_flushing_effect; + is_flushing = false; + + last_scheduled_effect = null; + if (DEV) { + dev_effect_stack = []; + } } } @@ -724,39 +716,17 @@ function flush_queued_effects(effects) { } } -function process_deferred() { - is_micro_task_queued = false; - if (flush_count > 1001) { - return; - } - const previous_queued_root_effects = queued_root_effects; - queued_root_effects = []; - flush_queued_root_effects(previous_queued_root_effects); - - if (!is_micro_task_queued) { - flush_count = 0; - last_scheduled_effect = null; - if (DEV) { - dev_effect_stack = []; - } - } -} - /** * @param {Effect} signal * @returns {void} */ export function schedule_effect(signal) { - if (scheduler_mode === FLUSH_MICROTASK) { - if (!is_micro_task_queued) { - is_micro_task_queued = true; - queueMicrotask(process_deferred); - } + if (!is_flushing) { + is_flushing = true; + queueMicrotask(flush_queued_root_effects); } - last_scheduled_effect = signal; - - var effect = signal; + var effect = (last_scheduled_effect = signal); while (effect.parent !== null) { effect = effect.parent; @@ -846,42 +816,30 @@ function process_effects(effect) { } /** - * Internal version of `flushSync` with the option to not flush previous effects. - * Returns the result of the passed function, if given. - * @param {() => any} [fn] - * @returns {any} + * Synchronously flush any pending updates. + * Returns void if no callback is provided, otherwise returns the result of calling the callback. + * @template [T=void] + * @param {(() => T) | undefined} [fn] + * @returns {T} */ -export function flush_sync(fn) { - var previous_scheduler_mode = scheduler_mode; - var previous_queued_root_effects = queued_root_effects; - - try { - infinite_loop_guard(); +export function flushSync(fn) { + var result; - scheduler_mode = FLUSH_SYNC; - queued_root_effects = []; - is_micro_task_queued = false; - - flush_queued_root_effects(previous_queued_root_effects); + if (fn) { + is_flushing = true; + flush_queued_root_effects(); + result = fn(); + } - var result = fn?.(); + flush_tasks(); + while (queued_root_effects.length > 0) { + is_flushing = true; + flush_queued_root_effects(); flush_tasks(); - if (queued_root_effects.length > 0) { - flush_sync(); - } - - flush_count = 0; - last_scheduled_effect = null; - if (DEV) { - dev_effect_stack = []; - } - - return result; - } finally { - scheduler_mode = previous_scheduler_mode; - queued_root_effects = previous_queued_root_effects; } + + return /** @type {T} */ (result); } /** @@ -890,9 +848,9 @@ export function flush_sync(fn) { */ export async function tick() { await Promise.resolve(); - // By calling flush_sync we guarantee that any pending state changes are applied after one tick. + // By calling flushSync we guarantee that any pending state changes are applied after one tick. // TODO look into whether we can make flushing subsequent updates synchronously in the future. - flush_sync(); + flushSync(); } /** @@ -994,7 +952,7 @@ export function safe_get(signal) { * @template T * @param {() => T} fn */ -export function capture_signals(fn) { +function capture_signals(fn) { var previous_captured_signals = captured_signals; captured_signals = new Set(); diff --git a/packages/svelte/src/legacy/legacy-client.js b/packages/svelte/src/legacy/legacy-client.js index 3a05bc04963f..bb9a5a9c039b 100644 --- a/packages/svelte/src/legacy/legacy-client.js +++ b/packages/svelte/src/legacy/legacy-client.js @@ -3,7 +3,7 @@ import { DIRTY, LEGACY_PROPS, MAYBE_DIRTY } from '../internal/client/constants.j import { user_pre_effect } from '../internal/client/reactivity/effects.js'; import { mutable_source, set } from '../internal/client/reactivity/sources.js'; import { hydrate, mount, unmount } from '../internal/client/render.js'; -import { active_effect, flush_sync, get, set_signal_status } from '../internal/client/runtime.js'; +import { active_effect, flushSync, get, set_signal_status } from '../internal/client/runtime.js'; import { lifecycle_outside_component } from '../internal/shared/errors.js'; import { define_property, is_array } from '../internal/shared/utils.js'; import * as w from '../internal/client/warnings.js'; @@ -119,9 +119,9 @@ class Svelte4Component { recover: options.recover }); - // We don't flush_sync for custom element wrappers or if the user doesn't want it + // We don't flushSync for custom element wrappers or if the user doesn't want it if (!options?.props?.$$host || options.sync === false) { - flush_sync(); + flushSync(); } this.#events = props.$$events; diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 12468d227d1a..e893def32688 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.20.3'; +export const VERSION = '5.20.4'; export const PUBLIC_VERSION = '5'; diff --git a/packages/svelte/tests/runtime-legacy/samples/animation-css/_config.js b/packages/svelte/tests/runtime-legacy/samples/animation-css/_config.js index b6b601a96b1d..b6bd818e65db 100644 --- a/packages/svelte/tests/runtime-legacy/samples/animation-css/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/animation-css/_config.js @@ -47,6 +47,8 @@ export default test({ { id: 1, name: 'a' } ]; + raf.tick(0); + divs = target.querySelectorAll('div'); assert.ok(divs[0].getAnimations().length > 0); assert.equal(divs[1].getAnimations().length, 0); diff --git a/packages/svelte/tests/runtime-legacy/samples/animation-js-easing/_config.js b/packages/svelte/tests/runtime-legacy/samples/animation-js-easing/_config.js index 5b7ed1c73209..f4a3554b29f9 100644 --- a/packages/svelte/tests/runtime-legacy/samples/animation-js-easing/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/animation-js-easing/_config.js @@ -46,6 +46,8 @@ export default test({ { id: 1, name: 'a' } ]; + raf.tick(0); + divs = document.querySelectorAll('div'); assert.equal(divs[0].dy, 120); assert.equal(divs[4].dy, -120); diff --git a/packages/svelte/tests/runtime-legacy/samples/animation-js/_config.js b/packages/svelte/tests/runtime-legacy/samples/animation-js/_config.js index 3606f7d17b77..a2e17b49f869 100644 --- a/packages/svelte/tests/runtime-legacy/samples/animation-js/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/animation-js/_config.js @@ -46,6 +46,8 @@ export default test({ { id: 1, name: 'a' } ]; + raf.tick(0); + divs = document.querySelectorAll('div'); assert.equal(divs[0].dy, 120); assert.equal(divs[4].dy, -120); @@ -66,6 +68,8 @@ export default test({ { id: 5, name: 'e' } ]; + raf.tick(100); + divs = document.querySelectorAll('div'); assert.equal(divs[0].dy, 120); diff --git a/packages/svelte/tests/runtime-legacy/samples/dynamic-element-animation/_config.js b/packages/svelte/tests/runtime-legacy/samples/dynamic-element-animation/_config.js index 3d127f1375e7..05c2dc73048a 100644 --- a/packages/svelte/tests/runtime-legacy/samples/dynamic-element-animation/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/dynamic-element-animation/_config.js @@ -50,6 +50,8 @@ export default test({ { id: 1, name: 'a' } ]; + raf.tick(0); + divs = target.querySelectorAll('div'); assert.equal(divs[0].style.transform, 'translate(0px, 120px)'); assert.equal(divs[1].style.transform, ''); diff --git a/packages/svelte/tests/snapshot/_config.js b/packages/svelte/tests/snapshot/_config.js deleted file mode 100644 index f47bee71df87..000000000000 --- a/packages/svelte/tests/snapshot/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -import { test } from '../../test'; - -export default test({}); diff --git a/packages/svelte/tests/store/test.ts b/packages/svelte/tests/store/test.ts index b23ea195d6c9..77cecca7e525 100644 --- a/packages/svelte/tests/store/test.ts +++ b/packages/svelte/tests/store/test.ts @@ -602,7 +602,7 @@ describe('toStore', () => { assert.deepEqual(log, [0]); set(count, 1); - $.flush_sync(); + $.flushSync(); assert.deepEqual(log, [0, 1]); unsubscribe(); @@ -625,7 +625,7 @@ describe('toStore', () => { assert.deepEqual(log, [0]); set(count, 1); - $.flush_sync(); + $.flushSync(); assert.deepEqual(log, [0, 1]); store.set(2); @@ -654,11 +654,11 @@ describe('fromStore', () => { assert.deepEqual(log, [0]); store.set(1); - $.flush_sync(); + $.flushSync(); assert.deepEqual(log, [0, 1]); count.current = 2; - $.flush_sync(); + $.flushSync(); assert.deepEqual(log, [0, 1, 2]); assert.equal(get(store), 2); diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index b9ab8a522ccb..4c47661af897 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -408,10 +408,6 @@ declare module 'svelte' { * @deprecated Use [`$effect`](https://svelte.dev/docs/svelte/$effect) instead * */ export function afterUpdate(fn: () => void): void; - /** - * Synchronously flushes any pending state changes and those that result from it. - * */ - export function flushSync(fn?: (() => void) | undefined): void; /** * Create a snippet programmatically * */ @@ -421,6 +417,29 @@ declare module 'svelte' { }): Snippet; /** Anything except a function */ type NotFunction = T extends Function ? never : T; + /** + * Synchronously flush any pending updates. + * Returns void if no callback is provided, otherwise returns the result of calling the callback. + * */ + export function flushSync(fn?: (() => T) | undefined): T; + /** + * Returns a promise that resolves once any pending state changes have been applied. + * */ + export function tick(): Promise; + /** + * When used inside a [`$derived`](https://svelte.dev/docs/svelte/$derived) or [`$effect`](https://svelte.dev/docs/svelte/$effect), + * any state read inside `fn` will not be treated as a dependency. + * + * ```ts + * $effect(() => { + * // this will run when `data` changes, but not when `time` changes + * save(data, { + * timestamp: untrack(() => time) + * }); + * }); + * ``` + * */ + export function untrack(fn: () => T): T; /** * Retrieves the context that belongs to the closest parent component with the specified `key`. * Must be called during component initialisation. @@ -494,24 +513,6 @@ declare module 'svelte' { export function unmount(component: Record, options?: { outro?: boolean; } | undefined): Promise; - /** - * Returns a promise that resolves once any pending state changes have been applied. - * */ - export function tick(): Promise; - /** - * When used inside a [`$derived`](https://svelte.dev/docs/svelte/$derived) or [`$effect`](https://svelte.dev/docs/svelte/$effect), - * any state read inside `fn` will not be treated as a dependency. - * - * ```ts - * $effect(() => { - * // this will run when `data` changes, but not when `time` changes - * save(data, { - * timestamp: untrack(() => time) - * }); - * }); - * ``` - * */ - export function untrack(fn: () => T): T; type Getters = { [K in keyof T]: () => T[K]; }; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy