Skip to content

Commit

Permalink
chore: move context code into new module (#15132)
Browse files Browse the repository at this point in the history
  • Loading branch information
Rich-Harris authored Jan 28, 2025
1 parent b2c8224 commit 5e9b29c
Show file tree
Hide file tree
Showing 21 changed files with 288 additions and 290 deletions.
15 changes: 4 additions & 11 deletions packages/svelte/src/index-client.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
/** @import { ComponentContext, ComponentContextLegacy } from '#client' */
/** @import { EventDispatcher } from './index.js' */
/** @import { NotFunction } from './internal/types.js' */
import { component_context, flush_sync, untrack } from './internal/client/runtime.js';
import { flush_sync, 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';
import { lifecycle_outside_component } from './internal/shared/errors.js';
import { legacy_mode_flag } from './internal/flags/index.js';
import { component_context } from './internal/client/context.js';

/**
* The `onMount` function schedules a callback to run as soon as the component has been mounted to the DOM.
Expand Down Expand Up @@ -179,15 +180,7 @@ export function flushSync(fn) {
flush_sync(fn);
}

export { getContext, getAllContexts, hasContext, setContext } from './internal/client/context.js';
export { hydrate, mount, unmount } from './internal/client/render.js';

export {
getContext,
getAllContexts,
hasContext,
setContext,
tick,
untrack
} from './internal/client/runtime.js';

export { tick, untrack } from './internal/client/runtime.js';
export { createRawSnippet } from './internal/client/dom/blocks/snippet.js';
214 changes: 214 additions & 0 deletions packages/svelte/src/internal/client/context.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
/** @import { ComponentContext } from '#client' */

import { DEV } from 'esm-env';
import { add_owner } from './dev/ownership.js';
import { lifecycle_outside_component } from '../shared/errors.js';
import { source } from './reactivity/sources.js';
import {
active_effect,
active_reaction,
set_active_effect,
set_active_reaction
} from './runtime.js';
import { effect } from './reactivity/effects.js';
import { legacy_mode_flag } from '../flags/index.js';

/** @type {ComponentContext | null} */
export let component_context = null;

/** @param {ComponentContext | null} context */
export function set_component_context(context) {
component_context = context;
}

/**
* The current component function. Different from current component context:
* ```html
* <!-- App.svelte -->
* <Foo>
* <Bar /> <!-- context == Foo.svelte, function == App.svelte -->
* </Foo>
* ```
* @type {ComponentContext['function']}
*/
export let dev_current_component_function = null;

/** @param {ComponentContext['function']} fn */
export function set_dev_current_component_function(fn) {
dev_current_component_function = fn;
}

/**
* Retrieves the context that belongs to the closest parent component with the specified `key`.
* Must be called during component initialisation.
*
* @template T
* @param {any} key
* @returns {T}
*/
export function getContext(key) {
const context_map = get_or_init_context_map('getContext');
const result = /** @type {T} */ (context_map.get(key));

if (DEV) {
const fn = /** @type {ComponentContext} */ (component_context).function;
if (fn) {
add_owner(result, fn, true);
}
}

return result;
}

/**
* Associates an arbitrary `context` object with the current component and the specified `key`
* and returns that object. The context is then available to children of the component
* (including slotted content) with `getContext`.
*
* Like lifecycle functions, this must be called during component initialisation.
*
* @template T
* @param {any} key
* @param {T} context
* @returns {T}
*/
export function setContext(key, context) {
const context_map = get_or_init_context_map('setContext');
context_map.set(key, context);
return context;
}

/**
* Checks whether a given `key` has been set in the context of a parent component.
* Must be called during component initialisation.
*
* @param {any} key
* @returns {boolean}
*/
export function hasContext(key) {
const context_map = get_or_init_context_map('hasContext');
return context_map.has(key);
}

/**
* Retrieves the whole context map that belongs to the closest parent component.
* Must be called during component initialisation. Useful, for example, if you
* programmatically create a component and want to pass the existing context to it.
*
* @template {Map<any, any>} [T=Map<any, any>]
* @returns {T}
*/
export function getAllContexts() {
const context_map = get_or_init_context_map('getAllContexts');

if (DEV) {
const fn = component_context?.function;
if (fn) {
for (const value of context_map.values()) {
add_owner(value, fn, true);
}
}
}

return /** @type {T} */ (context_map);
}

/**
* @param {Record<string, unknown>} props
* @param {any} runes
* @param {Function} [fn]
* @returns {void}
*/
export function push(props, runes = false, fn) {
component_context = {
p: component_context,
c: null,
e: null,
m: false,
s: props,
x: null,
l: null
};

if (legacy_mode_flag && !runes) {
component_context.l = {
s: null,
u: null,
r1: [],
r2: source(false)
};
}

if (DEV) {
// component function
component_context.function = fn;
dev_current_component_function = fn;
}
}

/**
* @template {Record<string, any>} T
* @param {T} [component]
* @returns {T}
*/
export function pop(component) {
const context_stack_item = component_context;
if (context_stack_item !== null) {
if (component !== undefined) {
context_stack_item.x = component;
}
const component_effects = context_stack_item.e;
if (component_effects !== null) {
var previous_effect = active_effect;
var previous_reaction = active_reaction;
context_stack_item.e = null;
try {
for (var i = 0; i < component_effects.length; i++) {
var component_effect = component_effects[i];
set_active_effect(component_effect.effect);
set_active_reaction(component_effect.reaction);
effect(component_effect.fn);
}
} finally {
set_active_effect(previous_effect);
set_active_reaction(previous_reaction);
}
}
component_context = context_stack_item.p;
if (DEV) {
dev_current_component_function = context_stack_item.p?.function ?? null;
}
context_stack_item.m = true;
}
// Micro-optimization: Don't set .a above to the empty object
// so it can be garbage-collected when the return here is unused
return component || /** @type {T} */ ({});
}

/**
* @param {string} name
* @returns {Map<unknown, unknown>}
*/
function get_or_init_context_map(name) {
if (component_context === null) {
lifecycle_outside_component(name);
}

return (component_context.c ??= new Map(get_parent_context(component_context) || undefined));
}

/**
* @param {ComponentContext} component_context
* @returns {Map<unknown, unknown> | null}
*/
function get_parent_context(component_context) {
let parent = component_context.p;
while (parent !== null) {
const context_map = parent.c;
if (context_map !== null) {
return context_map;
}
parent = parent.p;
}
return null;
}
2 changes: 1 addition & 1 deletion packages/svelte/src/internal/client/dev/legacy.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as e from '../errors.js';
import { component_context } from '../runtime.js';
import { component_context } from '../context.js';
import { FILENAME } from '../../../constants.js';
import { get_component } from './ownership.js';

Expand Down
2 changes: 1 addition & 1 deletion packages/svelte/src/internal/client/dev/ownership.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import { STATE_SYMBOL_METADATA } from '../constants.js';
import { render_effect, user_pre_effect } from '../reactivity/effects.js';
import { dev_current_component_function } from '../runtime.js';
import { dev_current_component_function } from '../context.js';
import { get_prototype_of } from '../../shared/utils.js';
import * as w from '../warnings.js';
import { FILENAME } from '../../../constants.js';
Expand Down
13 changes: 5 additions & 8 deletions packages/svelte/src/internal/client/dom/blocks/await.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,15 @@ 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, is_runes, 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';
import {
component_context,
flush_sync,
is_runes,
set_active_effect,
set_active_reaction,
set_component_context,
set_dev_current_component_function
} from '../../runtime.js';
import { hydrate_next, hydrate_node, hydrating } from '../hydration.js';
import { queue_micro_task } from '../task.js';
import { UNINITIALIZED } from '../../../../constants.js';
} from '../../context.js';

const PENDING = 0;
const THEN = 1;
Expand Down
3 changes: 1 addition & 2 deletions packages/svelte/src/internal/client/dom/blocks/boundary.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
/** @import { Effect, TemplateNode, } from '#client' */

import { BOUNDARY_EFFECT, EFFECT_TRANSPARENT } from '../../constants.js';
import { component_context, set_component_context } from '../../context.js';
import { block, branch, destroy_effect, pause_effect } from '../../reactivity/effects.js';
import {
active_effect,
active_reaction,
component_context,
handle_error,
set_active_effect,
set_active_reaction,
set_component_context,
reset_is_throwing_error
} from '../../runtime.js';
import {
Expand Down
2 changes: 1 addition & 1 deletion packages/svelte/src/internal/client/dom/blocks/html.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { assign_nodes } from '../template.js';
import * as w from '../../warnings.js';
import { hash, sanitize_location } from '../../../../utils.js';
import { DEV } from 'esm-env';
import { dev_current_component_function } from '../../runtime.js';
import { dev_current_component_function } from '../../context.js';
import { get_first_child, get_next_sibling } from '../operations.js';

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/svelte/src/internal/client/dom/blocks/snippet.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { branch, block, destroy_effect, teardown } from '../../reactivity/effect
import {
dev_current_component_function,
set_dev_current_component_function
} from '../../runtime.js';
} from '../../context.js';
import { hydrate_next, hydrate_node, hydrating } from '../hydration.js';
import { create_fragment_from_html } from '../reconciler.js';
import { assign_nodes } from '../template.js';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import {
} from '../../reactivity/effects.js';
import { set_should_intro } from '../../render.js';
import { current_each_item, set_current_each_item } from './each.js';
import { component_context, active_effect } from '../../runtime.js';
import { active_effect } from '../../runtime.js';
import { component_context } from '../../context.js';
import { DEV } from 'esm-env';
import { EFFECT_TRANSPARENT } from '../../constants.js';
import { assign_nodes } from '../template.js';
Expand Down
3 changes: 2 additions & 1 deletion packages/svelte/src/internal/client/dom/legacy/lifecycle.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/** @import { ComponentContextLegacy } from '#client' */
import { run, run_all } from '../../../shared/utils.js';
import { component_context } from '../../context.js';
import { derived } from '../../reactivity/deriveds.js';
import { user_pre_effect, user_effect } from '../../reactivity/effects.js';
import { component_context, deep_read_state, get, untrack } from '../../runtime.js';
import { deep_read_state, get, untrack } from '../../runtime.js';

/**
* Legacy-mode only: Call `onMount` callbacks and set up `beforeUpdate`/`afterUpdate` effects
Expand Down
9 changes: 2 additions & 7 deletions packages/svelte/src/internal/client/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { FILENAME, HMR, NAMESPACE_SVG } from '../../constants.js';
export { push, pop } from './context.js';
export { assign, assign_and, assign_or, assign_nullish } from './dev/assign.js';
export { cleanup_styles } from './dev/css.js';
export { add_locations } from './dev/elements.js';
Expand Down Expand Up @@ -141,14 +142,8 @@ export {
update,
update_pre,
exclude_from_object,
pop,
push,
deep_read,
deep_read_state,
getAllContexts,
getContext,
setContext,
hasContext
deep_read_state
} from './runtime.js';
export { validate_binding, validate_each_keys } from './validate.js';
export { raf } from './timing.js';
Expand Down
5 changes: 3 additions & 2 deletions packages/svelte/src/internal/client/proxy.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/** @import { ProxyMetadata, ProxyStateObject, Source } from '#client' */
/** @import { ProxyMetadata, Source } from '#client' */
import { DEV } from 'esm-env';
import { get, component_context, active_effect } from './runtime.js';
import { get, active_effect } from './runtime.js';
import { component_context } from './context.js';
import {
array_prototype,
get_descriptor,
Expand Down
4 changes: 2 additions & 2 deletions packages/svelte/src/internal/client/reactivity/deriveds.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ import {
skip_reaction,
update_reaction,
increment_write_version,
set_active_effect,
component_context
set_active_effect
} from '../runtime.js';
import { equals, safe_equals } from './equality.js';
import * as e from '../errors.js';
import { destroy_effect } from './effects.js';
import { inspect_effects, set_inspect_effects } from './sources.js';
import { get_stack } from '../dev/tracing.js';
import { tracing_mode_flag } from '../../flags/index.js';
import { component_context } from '../context.js';

/**
* @template V
Expand Down
Loading

0 comments on commit 5e9b29c

Please sign in to comment.