Skip to content

Commit 5e9b29c

Browse files
authored
chore: move context code into new module (#15132)
1 parent b2c8224 commit 5e9b29c

File tree

21 files changed

+288
-290
lines changed

21 files changed

+288
-290
lines changed

packages/svelte/src/index-client.js

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
/** @import { ComponentContext, ComponentContextLegacy } from '#client' */
22
/** @import { EventDispatcher } from './index.js' */
33
/** @import { NotFunction } from './internal/types.js' */
4-
import { component_context, flush_sync, untrack } from './internal/client/runtime.js';
4+
import { flush_sync, untrack } from './internal/client/runtime.js';
55
import { is_array } from './internal/shared/utils.js';
66
import { user_effect } from './internal/client/index.js';
77
import * as e from './internal/client/errors.js';
88
import { lifecycle_outside_component } from './internal/shared/errors.js';
99
import { legacy_mode_flag } from './internal/flags/index.js';
10+
import { component_context } from './internal/client/context.js';
1011

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

183+
export { getContext, getAllContexts, hasContext, setContext } from './internal/client/context.js';
182184
export { hydrate, mount, unmount } from './internal/client/render.js';
183-
184-
export {
185-
getContext,
186-
getAllContexts,
187-
hasContext,
188-
setContext,
189-
tick,
190-
untrack
191-
} from './internal/client/runtime.js';
192-
185+
export { tick, untrack } from './internal/client/runtime.js';
193186
export { createRawSnippet } from './internal/client/dom/blocks/snippet.js';
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
/** @import { ComponentContext } from '#client' */
2+
3+
import { DEV } from 'esm-env';
4+
import { add_owner } from './dev/ownership.js';
5+
import { lifecycle_outside_component } from '../shared/errors.js';
6+
import { source } from './reactivity/sources.js';
7+
import {
8+
active_effect,
9+
active_reaction,
10+
set_active_effect,
11+
set_active_reaction
12+
} from './runtime.js';
13+
import { effect } from './reactivity/effects.js';
14+
import { legacy_mode_flag } from '../flags/index.js';
15+
16+
/** @type {ComponentContext | null} */
17+
export let component_context = null;
18+
19+
/** @param {ComponentContext | null} context */
20+
export function set_component_context(context) {
21+
component_context = context;
22+
}
23+
24+
/**
25+
* The current component function. Different from current component context:
26+
* ```html
27+
* <!-- App.svelte -->
28+
* <Foo>
29+
* <Bar /> <!-- context == Foo.svelte, function == App.svelte -->
30+
* </Foo>
31+
* ```
32+
* @type {ComponentContext['function']}
33+
*/
34+
export let dev_current_component_function = null;
35+
36+
/** @param {ComponentContext['function']} fn */
37+
export function set_dev_current_component_function(fn) {
38+
dev_current_component_function = fn;
39+
}
40+
41+
/**
42+
* Retrieves the context that belongs to the closest parent component with the specified `key`.
43+
* Must be called during component initialisation.
44+
*
45+
* @template T
46+
* @param {any} key
47+
* @returns {T}
48+
*/
49+
export function getContext(key) {
50+
const context_map = get_or_init_context_map('getContext');
51+
const result = /** @type {T} */ (context_map.get(key));
52+
53+
if (DEV) {
54+
const fn = /** @type {ComponentContext} */ (component_context).function;
55+
if (fn) {
56+
add_owner(result, fn, true);
57+
}
58+
}
59+
60+
return result;
61+
}
62+
63+
/**
64+
* Associates an arbitrary `context` object with the current component and the specified `key`
65+
* and returns that object. The context is then available to children of the component
66+
* (including slotted content) with `getContext`.
67+
*
68+
* Like lifecycle functions, this must be called during component initialisation.
69+
*
70+
* @template T
71+
* @param {any} key
72+
* @param {T} context
73+
* @returns {T}
74+
*/
75+
export function setContext(key, context) {
76+
const context_map = get_or_init_context_map('setContext');
77+
context_map.set(key, context);
78+
return context;
79+
}
80+
81+
/**
82+
* Checks whether a given `key` has been set in the context of a parent component.
83+
* Must be called during component initialisation.
84+
*
85+
* @param {any} key
86+
* @returns {boolean}
87+
*/
88+
export function hasContext(key) {
89+
const context_map = get_or_init_context_map('hasContext');
90+
return context_map.has(key);
91+
}
92+
93+
/**
94+
* Retrieves the whole context map that belongs to the closest parent component.
95+
* Must be called during component initialisation. Useful, for example, if you
96+
* programmatically create a component and want to pass the existing context to it.
97+
*
98+
* @template {Map<any, any>} [T=Map<any, any>]
99+
* @returns {T}
100+
*/
101+
export function getAllContexts() {
102+
const context_map = get_or_init_context_map('getAllContexts');
103+
104+
if (DEV) {
105+
const fn = component_context?.function;
106+
if (fn) {
107+
for (const value of context_map.values()) {
108+
add_owner(value, fn, true);
109+
}
110+
}
111+
}
112+
113+
return /** @type {T} */ (context_map);
114+
}
115+
116+
/**
117+
* @param {Record<string, unknown>} props
118+
* @param {any} runes
119+
* @param {Function} [fn]
120+
* @returns {void}
121+
*/
122+
export function push(props, runes = false, fn) {
123+
component_context = {
124+
p: component_context,
125+
c: null,
126+
e: null,
127+
m: false,
128+
s: props,
129+
x: null,
130+
l: null
131+
};
132+
133+
if (legacy_mode_flag && !runes) {
134+
component_context.l = {
135+
s: null,
136+
u: null,
137+
r1: [],
138+
r2: source(false)
139+
};
140+
}
141+
142+
if (DEV) {
143+
// component function
144+
component_context.function = fn;
145+
dev_current_component_function = fn;
146+
}
147+
}
148+
149+
/**
150+
* @template {Record<string, any>} T
151+
* @param {T} [component]
152+
* @returns {T}
153+
*/
154+
export function pop(component) {
155+
const context_stack_item = component_context;
156+
if (context_stack_item !== null) {
157+
if (component !== undefined) {
158+
context_stack_item.x = component;
159+
}
160+
const component_effects = context_stack_item.e;
161+
if (component_effects !== null) {
162+
var previous_effect = active_effect;
163+
var previous_reaction = active_reaction;
164+
context_stack_item.e = null;
165+
try {
166+
for (var i = 0; i < component_effects.length; i++) {
167+
var component_effect = component_effects[i];
168+
set_active_effect(component_effect.effect);
169+
set_active_reaction(component_effect.reaction);
170+
effect(component_effect.fn);
171+
}
172+
} finally {
173+
set_active_effect(previous_effect);
174+
set_active_reaction(previous_reaction);
175+
}
176+
}
177+
component_context = context_stack_item.p;
178+
if (DEV) {
179+
dev_current_component_function = context_stack_item.p?.function ?? null;
180+
}
181+
context_stack_item.m = true;
182+
}
183+
// Micro-optimization: Don't set .a above to the empty object
184+
// so it can be garbage-collected when the return here is unused
185+
return component || /** @type {T} */ ({});
186+
}
187+
188+
/**
189+
* @param {string} name
190+
* @returns {Map<unknown, unknown>}
191+
*/
192+
function get_or_init_context_map(name) {
193+
if (component_context === null) {
194+
lifecycle_outside_component(name);
195+
}
196+
197+
return (component_context.c ??= new Map(get_parent_context(component_context) || undefined));
198+
}
199+
200+
/**
201+
* @param {ComponentContext} component_context
202+
* @returns {Map<unknown, unknown> | null}
203+
*/
204+
function get_parent_context(component_context) {
205+
let parent = component_context.p;
206+
while (parent !== null) {
207+
const context_map = parent.c;
208+
if (context_map !== null) {
209+
return context_map;
210+
}
211+
parent = parent.p;
212+
}
213+
return null;
214+
}

packages/svelte/src/internal/client/dev/legacy.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as e from '../errors.js';
2-
import { component_context } from '../runtime.js';
2+
import { component_context } from '../context.js';
33
import { FILENAME } from '../../../constants.js';
44
import { get_component } from './ownership.js';
55

packages/svelte/src/internal/client/dev/ownership.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import { STATE_SYMBOL_METADATA } from '../constants.js';
55
import { render_effect, user_pre_effect } from '../reactivity/effects.js';
6-
import { dev_current_component_function } from '../runtime.js';
6+
import { dev_current_component_function } from '../context.js';
77
import { get_prototype_of } from '../../shared/utils.js';
88
import * as w from '../warnings.js';
99
import { FILENAME } from '../../../constants.js';

packages/svelte/src/internal/client/dom/blocks/await.js

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,15 @@ import { DEV } from 'esm-env';
33
import { is_promise } from '../../../shared/utils.js';
44
import { block, branch, pause_effect, resume_effect } from '../../reactivity/effects.js';
55
import { internal_set, mutable_source, source } from '../../reactivity/sources.js';
6+
import { flush_sync, is_runes, set_active_effect, set_active_reaction } from '../../runtime.js';
7+
import { hydrate_next, hydrate_node, hydrating } from '../hydration.js';
8+
import { queue_micro_task } from '../task.js';
9+
import { UNINITIALIZED } from '../../../../constants.js';
610
import {
711
component_context,
8-
flush_sync,
9-
is_runes,
10-
set_active_effect,
11-
set_active_reaction,
1212
set_component_context,
1313
set_dev_current_component_function
14-
} from '../../runtime.js';
15-
import { hydrate_next, hydrate_node, hydrating } from '../hydration.js';
16-
import { queue_micro_task } from '../task.js';
17-
import { UNINITIALIZED } from '../../../../constants.js';
14+
} from '../../context.js';
1815

1916
const PENDING = 0;
2017
const THEN = 1;

packages/svelte/src/internal/client/dom/blocks/boundary.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
/** @import { Effect, TemplateNode, } from '#client' */
22

33
import { BOUNDARY_EFFECT, EFFECT_TRANSPARENT } from '../../constants.js';
4+
import { component_context, set_component_context } from '../../context.js';
45
import { block, branch, destroy_effect, pause_effect } from '../../reactivity/effects.js';
56
import {
67
active_effect,
78
active_reaction,
8-
component_context,
99
handle_error,
1010
set_active_effect,
1111
set_active_reaction,
12-
set_component_context,
1312
reset_is_throwing_error
1413
} from '../../runtime.js';
1514
import {

packages/svelte/src/internal/client/dom/blocks/html.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { assign_nodes } from '../template.js';
77
import * as w from '../../warnings.js';
88
import { hash, sanitize_location } from '../../../../utils.js';
99
import { DEV } from 'esm-env';
10-
import { dev_current_component_function } from '../../runtime.js';
10+
import { dev_current_component_function } from '../../context.js';
1111
import { get_first_child, get_next_sibling } from '../operations.js';
1212

1313
/**

packages/svelte/src/internal/client/dom/blocks/snippet.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { branch, block, destroy_effect, teardown } from '../../reactivity/effect
66
import {
77
dev_current_component_function,
88
set_dev_current_component_function
9-
} from '../../runtime.js';
9+
} from '../../context.js';
1010
import { hydrate_next, hydrate_node, hydrating } from '../hydration.js';
1111
import { create_fragment_from_html } from '../reconciler.js';
1212
import { assign_nodes } from '../template.js';

packages/svelte/src/internal/client/dom/blocks/svelte-element.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ import {
1717
} from '../../reactivity/effects.js';
1818
import { set_should_intro } from '../../render.js';
1919
import { current_each_item, set_current_each_item } from './each.js';
20-
import { component_context, active_effect } from '../../runtime.js';
20+
import { active_effect } from '../../runtime.js';
21+
import { component_context } from '../../context.js';
2122
import { DEV } from 'esm-env';
2223
import { EFFECT_TRANSPARENT } from '../../constants.js';
2324
import { assign_nodes } from '../template.js';

packages/svelte/src/internal/client/dom/legacy/lifecycle.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
/** @import { ComponentContextLegacy } from '#client' */
22
import { run, run_all } from '../../../shared/utils.js';
3+
import { component_context } from '../../context.js';
34
import { derived } from '../../reactivity/deriveds.js';
45
import { user_pre_effect, user_effect } from '../../reactivity/effects.js';
5-
import { component_context, deep_read_state, get, untrack } from '../../runtime.js';
6+
import { deep_read_state, get, untrack } from '../../runtime.js';
67

78
/**
89
* Legacy-mode only: Call `onMount` callbacks and set up `beforeUpdate`/`afterUpdate` effects

0 commit comments

Comments
 (0)