preserve context

aaa
Rich Harris 8 months ago
parent b788ec059a
commit 964004a1b0

@ -7,10 +7,15 @@ import * as b from '../../../../utils/builders.js';
* @param {ComponentContext} context * @param {ComponentContext} context
*/ */
export function AwaitExpression(node, context) { export function AwaitExpression(node, context) {
return b.await( return b.call(
b.member(
b.await(
b.call( b.call(
'$.preserve_context', '$.preserve_context',
node.argument && /** @type {Expression} */ (context.visit(node.argument)) node.argument && /** @type {Expression} */ (context.visit(node.argument))
) )
),
'read'
)
); );
} }

@ -21,6 +21,8 @@ export const INSPECT_EFFECT = 1 << 18;
export const HEAD_EFFECT = 1 << 19; export const HEAD_EFFECT = 1 << 19;
export const EFFECT_HAS_DERIVED = 1 << 20; export const EFFECT_HAS_DERIVED = 1 << 20;
export const REACTION_IS_UPDATING = 1 << 21;
export const STATE_SYMBOL = Symbol('$state'); export const STATE_SYMBOL = Symbol('$state');
export const STATE_SYMBOL_METADATA = Symbol('$state metadata'); export const STATE_SYMBOL_METADATA = Symbol('$state metadata');
export const LEGACY_PROPS = Symbol('legacy props'); export const LEGACY_PROPS = Symbol('legacy props');

@ -261,26 +261,27 @@ export function create_suspense() {
/** /**
* @template T * @template T
* @param {Promise<T>} promise * @param {Promise<T>} promise
* @returns {Promise<T>} * @returns {Promise<{ read: () => T }>}
*/ */
export async function preserve_context(promise) { export async function preserve_context(promise) {
if (!active_effect) {
return promise;
}
var previous_effect = active_effect; var previous_effect = active_effect;
var previous_reaction = active_reaction; var previous_reaction = active_reaction;
var previous_component_context = component_context; var previous_component_context = component_context;
const [suspend, unsuspend] = create_suspense(); const [suspend, unsuspend] = create_suspense();
try {
suspend(); suspend();
return await promise;
} finally { const value = await promise;
return {
read() {
set_active_effect(previous_effect); set_active_effect(previous_effect);
set_active_reaction(previous_reaction); set_active_reaction(previous_reaction);
set_component_context(previous_component_context); set_component_context(previous_component_context);
unsuspend(); unsuspend();
return value;
} }
};
} }

@ -25,7 +25,8 @@ import {
ROOT_EFFECT, ROOT_EFFECT,
LEGACY_DERIVED_PROP, LEGACY_DERIVED_PROP,
DISCONNECTED, DISCONNECTED,
BOUNDARY_EFFECT BOUNDARY_EFFECT,
REACTION_IS_UPDATING
} from './constants.js'; } from './constants.js';
import { flush_tasks } from './dom/task.js'; import { flush_tasks } from './dom/task.js';
import { add_owner } from './dev/ownership.js'; import { add_owner } from './dev/ownership.js';
@ -435,6 +436,7 @@ export function update_reaction(reaction) {
read_version++; read_version++;
try { try {
reaction.f |= REACTION_IS_UPDATING;
var result = /** @type {Function} */ (0, reaction.fn)(); var result = /** @type {Function} */ (0, reaction.fn)();
var deps = reaction.deps; var deps = reaction.deps;
@ -488,6 +490,7 @@ export function update_reaction(reaction) {
return result; return result;
} finally { } finally {
reaction.f ^= REACTION_IS_UPDATING;
new_deps = previous_deps; new_deps = previous_deps;
skipped_deps = previous_skipped_deps; skipped_deps = previous_skipped_deps;
untracked_writes = previous_untracked_writes; untracked_writes = previous_untracked_writes;
@ -776,7 +779,7 @@ export function schedule_effect(signal) {
var flags = effect.f; var flags = effect.f;
if ((flags & (ROOT_EFFECT | BRANCH_EFFECT)) !== 0) { if ((flags & (ROOT_EFFECT | BRANCH_EFFECT)) !== 0) {
if ((flags & CLEAN) === 0) return if ((flags & CLEAN) === 0) return;
effect.f ^= CLEAN; effect.f ^= CLEAN;
} }
} }
@ -938,9 +941,14 @@ export function get(signal) {
if (derived_sources !== null && derived_sources.includes(signal)) { if (derived_sources !== null && derived_sources.includes(signal)) {
e.state_unsafe_local_read(); e.state_unsafe_local_read();
} }
var deps = active_reaction.deps; var deps = active_reaction.deps;
if ((active_reaction.f & REACTION_IS_UPDATING) !== 0) {
// we're in the effect init/update cycle
if (signal.rv < read_version) { if (signal.rv < read_version) {
signal.rv = read_version; signal.rv = read_version;
// If the signal is accessing the same dependencies in the same // If the signal is accessing the same dependencies in the same
// order as it did last time, increment `skipped_deps` // order as it did last time, increment `skipped_deps`
// rather than updating `new_deps`, which creates GC cost // rather than updating `new_deps`, which creates GC cost
@ -952,6 +960,23 @@ export function get(signal) {
new_deps.push(signal); new_deps.push(signal);
} }
} }
} else {
// we're adding a dependency outside the init/update cycle
// (i.e. after an `await`)
// TODO we probably want to disable this for user effects,
// otherwise it's a breaking change, albeit a desirable one?
if (deps === null) {
deps = [signal];
} else if (!deps.includes(signal)) {
deps.push(signal);
}
if (signal.reactions === null) {
signal.reactions = [active_reaction];
} else if (!signal.reactions.includes(active_reaction)) {
signal.reactions.push(active_reaction);
}
}
} else if (is_derived && /** @type {Derived} */ (signal).deps === null) { } else if (is_derived && /** @type {Derived} */ (signal).deps === null) {
var derived = /** @type {Derived} */ (signal); var derived = /** @type {Derived} */ (signal);
var parent = derived.parent; var parent = derived.parent;

@ -11,7 +11,7 @@ export default defineConfig({
inspect(), inspect(),
svelte({ svelte({
compilerOptions: { compilerOptions: {
hmr: true hmr: false
} }
}) })
], ],

Loading…
Cancel
Save