svelte
Version:
Cybernetically enhanced web apps
227 lines (198 loc) • 6.36 kB
JavaScript
/** @import { ComponentContext, DevStackEntry, Effect } from '#client' */
import { DEV } from 'esm-env';
import * as e from './errors.js';
import { active_effect, active_reaction } from './runtime.js';
import { create_user_effect } from './reactivity/effects.js';
import { async_mode_flag, legacy_mode_flag } from '../flags/index.js';
import { FILENAME } from '../../constants.js';
import { BRANCH_EFFECT } from './constants.js';
import { create_context, get_or_init_context_map } from '../shared/context.js';
/** @type {ComponentContext | null} */
export let component_context = null;
/** @param {ComponentContext | null} context */
export function set_component_context(context) {
component_context = context;
}
/** @type {DevStackEntry | null} */
export let dev_stack = null;
/** @param {DevStackEntry | null} stack */
export function set_dev_stack(stack) {
dev_stack = stack;
}
/**
* Execute a callback with a new dev stack entry
* @param {() => any} callback - Function to execute
* @param {DevStackEntry['type']} type - Type of block/component
* @param {any} component - Component function
* @param {number} line - Line number
* @param {number} column - Column number
* @param {Record<string, any>} [additional] - Any additional properties to add to the dev stack entry
* @returns {any}
*/
export function add_svelte_meta(callback, type, component, line, column, additional) {
const parent = dev_stack;
dev_stack = {
type,
file: component[FILENAME],
line,
column,
parent,
...additional
};
try {
return callback();
} finally {
dev_stack = parent;
}
}
/**
* 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;
}
/**
* Returns a `[get, set]` pair of functions for working with context in a type-safe way.
*
* `get` will throw an error if `set` has not yet been called in the current component or any of
* its ancestors.
*
* @template T
* @returns {[() => T, (context: T) => T]}
* @since 5.40.0
*/
export function createContext() {
return /** @type {[() => T, (context: T) => T]} */ (
create_context(getContext, setContext, hasContext)
);
}
/**
* Retrieves the context set with the specified `key` in the current component or any of its
* ancestors. If multiple components set the same key, the value from the closest one is returned.
* A `setContext` call in the current component is only visible to `getContext` calls that run after it.
* Must be called during component initialisation.
*
* [`createContext`](https://svelte.dev/docs/svelte/svelte#createContext) is a type-safe alternative.
*
* @template T
* @param {any} key
* @returns {T}
*/
export function getContext(key) {
const context_map = get_or_init_context_map(component_context, 'getContext');
const result = /** @type {T} */ (context_map.get(key));
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 the component itself and all of its
* descendants (including slotted content) with `getContext`.
*
* Like lifecycle functions, this must be called during component initialisation.
*
* [`createContext`](https://svelte.dev/docs/svelte/svelte#createContext) is a type-safe alternative.
*
* @template T
* @param {any} key
* @param {T} context
* @returns {T}
*/
export function setContext(key, context) {
const context_map = get_or_init_context_map(component_context, 'setContext');
if (async_mode_flag) {
var flags = /** @type {Effect} */ (active_effect).f;
var valid =
!active_reaction &&
(flags & BRANCH_EFFECT) !== 0 &&
// pop() runs synchronously, so this indicates we're setting context after an await
!(/** @type {ComponentContext} */ (component_context).i);
if (!valid) {
e.set_context_after_init();
}
}
context_map.set(key, context);
return context;
}
/**
* Checks whether a given `key` has been set in the context of the current component or any of
* its ancestors. Must be called during component initialisation.
*
* @param {any} key
* @returns {boolean}
*/
export function hasContext(key) {
const context_map = get_or_init_context_map(component_context, 'hasContext');
return context_map.has(key);
}
/**
* Retrieves the whole context map that belongs to the current component, including entries
* inherited from its ancestors. 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(component_context, 'getAllContexts');
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,
i: false,
c: null,
e: null,
s: props,
x: null,
r: /** @type {Effect} */ (active_effect),
l: legacy_mode_flag && !runes ? { s: null, u: null, $: [] } : null
};
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) {
var context = /** @type {ComponentContext} */ (component_context);
var effects = context.e;
if (effects !== null) {
context.e = null;
for (var fn of effects) {
create_user_effect(fn);
}
}
if (component !== undefined) {
context.x = component;
}
context.i = true;
component_context = context.p;
if (DEV) {
dev_current_component_function = component_context?.function ?? null;
}
return component ?? /** @type {T} */ ({});
}
/** @returns {boolean} */
export function is_runes() {
return !legacy_mode_flag || (component_context !== null && component_context.l === null);
}