From 1f04045052fabf2a3b20c77b0fedad97b2f2563a Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 24 Apr 2024 04:57:50 -0400 Subject: [PATCH] chore: move legacy context stuff into its own object (#11298) --- packages/svelte/src/index-client.js | 15 ++--- .../internal/client/dom/legacy/lifecycle.js | 12 ++-- .../src/internal/client/reactivity/effects.js | 14 ++--- .../src/internal/client/reactivity/sources.js | 4 +- .../svelte/src/internal/client/runtime.js | 31 ++++------ .../svelte/src/internal/client/types.d.ts | 62 ++++++++++++------- 6 files changed, 75 insertions(+), 63 deletions(-) diff --git a/packages/svelte/src/index-client.js b/packages/svelte/src/index-client.js index ba810969257e..83fede3863d1 100644 --- a/packages/svelte/src/index-client.js +++ b/packages/svelte/src/index-client.js @@ -21,13 +21,13 @@ export function onMount(fn) { throw new Error('onMount can only be used during component initialisation.'); } - if (current_component_context.r) { + if (current_component_context.l !== null) { + init_update_callbacks(current_component_context).m.push(fn); + } else { user_effect(() => { const cleanup = untrack(fn); if (typeof cleanup === 'function') return /** @type {() => void} */ (cleanup); }); - } else { - init_update_callbacks(current_component_context).m.push(fn); } } @@ -129,7 +129,7 @@ export function beforeUpdate(fn) { throw new Error('beforeUpdate can only be used during component initialisation'); } - if (current_component_context.r) { + if (current_component_context.l === null) { throw new Error('beforeUpdate cannot be used in runes mode'); } @@ -153,7 +153,7 @@ export function afterUpdate(fn) { throw new Error('afterUpdate can only be used during component initialisation.'); } - if (current_component_context.r) { + if (current_component_context.l === null) { throw new Error('afterUpdate cannot be used in runes mode'); } @@ -162,10 +162,11 @@ export function afterUpdate(fn) { /** * Legacy-mode: Init callbacks object for onMount/beforeUpdate/afterUpdate - * @param {import('./internal/client/types.js').ComponentContext} context + * @param {import('#client').ComponentContext} context */ function init_update_callbacks(context) { - return (context.u ??= { a: [], b: [], m: [] }); + var l = /** @type {import('#client').ComponentContextLegacy} */ (context).l; + return (l.u ??= { a: [], b: [], m: [] }); } /** diff --git a/packages/svelte/src/internal/client/dom/legacy/lifecycle.js b/packages/svelte/src/internal/client/dom/legacy/lifecycle.js index c8a2b72d69d7..ae4c66ccbbce 100644 --- a/packages/svelte/src/internal/client/dom/legacy/lifecycle.js +++ b/packages/svelte/src/internal/client/dom/legacy/lifecycle.js @@ -14,9 +14,11 @@ import { * Legacy-mode only: Call `onMount` callbacks and set up `beforeUpdate`/`afterUpdate` effects */ export function init() { - const context = /** @type {import('#client').ComponentContext} */ (current_component_context); - const callbacks = context.u; + const context = /** @type {import('#client').ComponentContextLegacy} */ ( + current_component_context + ); + const callbacks = context.l.u; if (!callbacks) return; // beforeUpdate @@ -58,11 +60,11 @@ export function init() { /** * Invoke the getter of all signals associated with a component * so they can be registered to the effect this function is called in. - * @param {import('#client').ComponentContext} context + * @param {import('#client').ComponentContextLegacy} context */ function observe_all(context) { - if (context.d) { - for (const signal of context.d) get(signal); + if (context.l.s) { + for (const signal of context.l.s) get(signal); } deep_read_state(context.s); diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 515d13136eb1..2d2ec5e19c72 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -182,11 +182,11 @@ export function effect(fn) { * @param {() => void | (() => void)} fn */ export function legacy_pre_effect(deps, fn) { - var context = /** @type {import('#client').ComponentContext} */ (current_component_context); + var context = /** @type {import('#client').ComponentContextLegacy} */ (current_component_context); /** @type {{ effect: null | import('#client').Effect, ran: boolean }} */ var token = { effect: null, ran: false }; - context.l1.push(token); + context.l.r1.push(token); token.effect = render_effect(() => { deps(); @@ -196,19 +196,19 @@ export function legacy_pre_effect(deps, fn) { if (token.ran) return; token.ran = true; - set(context.l2, true); + set(context.l.r2, true); untrack(fn); }); } export function legacy_pre_effect_reset() { - var context = /** @type {import('#client').ComponentContext} */ (current_component_context); + var context = /** @type {import('#client').ComponentContextLegacy} */ (current_component_context); render_effect(() => { - if (!get(context.l2)) return; + if (!get(context.l.r2)) return; // Run dirty `$:` statements - for (var token of context.l1) { + for (var token of context.l.r1) { var effect = token.effect; if (check_dirtiness(effect)) { @@ -218,7 +218,7 @@ export function legacy_pre_effect_reset() { token.ran = false; } - context.l2.v = false; // set directly to avoid rerunning this effect + context.l.r2.v = false; // set directly to avoid rerunning this effect }); } diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index 6ad3240d5e35..1b955c42dc8d 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -55,8 +55,8 @@ export function mutable_source(initial_value) { // bind the signal to the component context, in case we need to // track updates to trigger beforeUpdate/afterUpdate callbacks - if (current_component_context) { - (current_component_context.d ??= []).push(s); + if (current_component_context !== null && current_component_context.l !== null) { + (current_component_context.l.s ??= []).push(s); } return s; diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 22038cce70e1..e8233fbc90b0 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -115,7 +115,7 @@ export function set_current_component_context(context) { /** @returns {boolean} */ export function is_runes() { - return current_component_context !== null && current_component_context.r; + return current_component_context !== null && current_component_context.l === null; } /** @@ -1043,29 +1043,24 @@ export async function value_or_fallback_async(value, fallback) { */ export function push(props, runes = false, fn) { current_component_context = { - // exports (and props, if `accessors: true`) - x: null, - // context + p: current_component_context, c: null, - // effects e: null, - // mounted m: false, - // parent - p: current_component_context, - // signals - d: null, - // props s: props, - // runes - r: runes, - // legacy $: - l1: [], - l2: source(false), - // update_callbacks - u: null + x: null, + l: null }; + if (!runes) { + current_component_context.l = { + s: null, + u: null, + r1: [], + r2: source(false) + }; + } + if (DEV) { // component function // @ts-expect-error diff --git a/packages/svelte/src/internal/client/types.d.ts b/packages/svelte/src/internal/client/types.d.ts index e1568494a19d..dc9672bc0dd0 100644 --- a/packages/svelte/src/internal/client/types.d.ts +++ b/packages/svelte/src/internal/client/types.d.ts @@ -10,37 +10,51 @@ export type EventCallbackMap = Record; // when the JS VM JITs the code. export type ComponentContext = { - /** local signals (needed for beforeUpdate/afterUpdate) */ - d: null | Source[]; - /** props */ - s: Record; - /** exports (and props, if `accessors: true`) */ - x: Record | null; - /** deferred effects */ - e: null | Array<() => void | (() => void)>; - /** mounted */ - m: boolean; /** parent */ p: null | ComponentContext; /** context */ c: null | Map; - /** runes */ - r: boolean; - /** legacy mode: if `$:` statements are allowed to run (ensures they only run once per render) */ - l1: any[]; - /** legacy mode: if `$:` statements are allowed to run (ensures they only run once per render) */ - l2: Source; - /** update_callbacks */ - u: null | { - /** afterUpdate callbacks */ - a: Array<() => void>; - /** beforeUpdate callbacks */ - b: Array<() => void>; - /** onMount callbacks */ - m: Array<() => any>; + /** deferred effects */ + e: null | Array<() => void | (() => void)>; + /** mounted */ + m: boolean; + /** + * props — needed for legacy mode lifecycle functions, and for `createEventDispatcher` + * @deprecated remove in 6.0 + */ + s: Record; + /** + * exports (and props, if `accessors: true`) — needed for `createEventDispatcher` + * @deprecated remove in 6.0 + */ + x: Record | null; + /** + * legacy stuff + * @deprecated remove in 6.0 + */ + l: null | { + /** local signals (needed for beforeUpdate/afterUpdate) */ + s: null | Source[]; + /** update_callbacks */ + u: null | { + /** afterUpdate callbacks */ + a: Array<() => void>; + /** beforeUpdate callbacks */ + b: Array<() => void>; + /** onMount callbacks */ + m: Array<() => any>; + }; + /** `$:` statements */ + r1: any[]; + /** This tracks whether `$:` statements have run in the current cycle, to ensure they only run once */ + r2: Source; }; }; +export type ComponentContextLegacy = ComponentContext & { + l: NonNullable; +}; + export type Equals = (this: Value, value: unknown) => boolean; export type TemplateNode = Text | Element | Comment;