-
-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: move context code into new module (#15132)
- Loading branch information
1 parent
b2c8224
commit 5e9b29c
Showing
21 changed files
with
288 additions
and
290 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.