Skip to content

chore: move legacy context stuff into its own object #11298

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions packages/svelte/src/index-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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');
}

Expand All @@ -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');
}

Expand All @@ -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: [] });
}

/**
Expand Down
12 changes: 7 additions & 5 deletions packages/svelte/src/internal/client/dom/legacy/lifecycle.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
14 changes: 7 additions & 7 deletions packages/svelte/src/internal/client/reactivity/effects.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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)) {
Expand All @@ -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
});
}

Expand Down
4 changes: 2 additions & 2 deletions packages/svelte/src/internal/client/reactivity/sources.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
31 changes: 13 additions & 18 deletions packages/svelte/src/internal/client/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -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
Expand Down
62 changes: 38 additions & 24 deletions packages/svelte/src/internal/client/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,51 @@ export type EventCallbackMap = Record<string, EventCallback | EventCallback[]>;
// when the JS VM JITs the code.

export type ComponentContext = {
/** local signals (needed for beforeUpdate/afterUpdate) */
d: null | Source[];
/** props */
s: Record<string, unknown>;
/** exports (and props, if `accessors: true`) */
x: Record<string, any> | null;
/** deferred effects */
e: null | Array<() => void | (() => void)>;
/** mounted */
m: boolean;
/** parent */
p: null | ComponentContext;
/** context */
c: null | Map<unknown, unknown>;
/** 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<boolean>;
/** 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<string, unknown>;
/**
* exports (and props, if `accessors: true`) — needed for `createEventDispatcher`
* @deprecated remove in 6.0
*/
x: Record<string, any> | 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<boolean>;
};
};

export type ComponentContextLegacy = ComponentContext & {
l: NonNullable<ComponentContext['l']>;
};

export type Equals = (this: Value, value: unknown) => boolean;

export type TemplateNode = Text | Element | Comment;
Expand Down