chore: deduplicate client/server context helpers (#18580)

The server copy of `createContext` shipped without the `missing_context`
throw and #17580 had to hand-mirror the client body back in, so this
duplication has already cost a bug. This moves the realm-independent
parts, the `createContext` tuple, `get_parent_context`, and
`get_or_init_context_map`, into `internal/shared/context.js`, and each
realm keeps its public context functions as thin wrappers over its own
state.
pull/18537/merge
Nic Polumeyv 1 week ago committed by GitHub
parent 388840b1b2
commit 3feb34a992
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
chore: deduplicate client and server context helpers

@ -6,6 +6,7 @@ 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;
@ -79,18 +80,9 @@ export function set_dev_current_component_function(fn) {
* @since 5.40.0
*/
export function createContext() {
const key = {};
return [
() => {
if (!hasContext(key)) {
e.missing_context();
}
return getContext(key);
},
(context) => setContext(key, context)
];
return /** @type {[() => T, (context: T) => T]} */ (
create_context(getContext, setContext, hasContext)
);
}
/**
@ -104,7 +96,7 @@ export function createContext() {
* @returns {T}
*/
export function getContext(key) {
const context_map = get_or_init_context_map('getContext');
const context_map = get_or_init_context_map(component_context, 'getContext');
const result = /** @type {T} */ (context_map.get(key));
return result;
}
@ -124,7 +116,7 @@ export function getContext(key) {
* @returns {T}
*/
export function setContext(key, context) {
const context_map = get_or_init_context_map('setContext');
const context_map = get_or_init_context_map(component_context, 'setContext');
if (async_mode_flag) {
var flags = /** @type {Effect} */ (active_effect).f;
@ -151,7 +143,7 @@ export function setContext(key, context) {
* @returns {boolean}
*/
export function hasContext(key) {
const context_map = get_or_init_context_map('hasContext');
const context_map = get_or_init_context_map(component_context, 'hasContext');
return context_map.has(key);
}
@ -164,7 +156,7 @@ export function hasContext(key) {
* @returns {T}
*/
export function getAllContexts() {
const context_map = get_or_init_context_map('getAllContexts');
const context_map = get_or_init_context_map(component_context, 'getAllContexts');
return /** @type {T} */ (context_map);
}
@ -229,31 +221,3 @@ export function pop(component) {
export function is_runes() {
return !legacy_mode_flag || (component_context !== null && component_context.l === null);
}
/**
* @param {string} name
* @returns {Map<unknown, unknown>}
*/
function get_or_init_context_map(name) {
if (component_context === null) {
e.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;
}

@ -1,6 +1,6 @@
/** @import { SSRContext } from '#server' */
import { DEV } from 'esm-env';
import * as e from './errors.js';
import { create_context, get_or_init_context_map } from '../shared/context.js';
/** @type {SSRContext | null} */
export var ssr_context = null;
@ -16,18 +16,9 @@ export function set_ssr_context(v) {
* @since 5.40.0
*/
export function createContext() {
const key = {};
return [
() => {
if (!hasContext(key)) {
e.missing_context();
}
return getContext(key);
},
(context) => setContext(key, context)
];
return /** @type {[() => T, (context: T) => T]} */ (
create_context(getContext, setContext, hasContext)
);
}
/**
@ -36,7 +27,7 @@ export function createContext() {
* @returns {T}
*/
export function getContext(key) {
const context_map = get_or_init_context_map('getContext');
const context_map = get_or_init_context_map(ssr_context, 'getContext');
const result = /** @type {T} */ (context_map.get(key));
return result;
@ -49,7 +40,7 @@ export function getContext(key) {
* @returns {T}
*/
export function setContext(key, context) {
get_or_init_context_map('setContext').set(key, context);
get_or_init_context_map(ssr_context, 'setContext').set(key, context);
return context;
}
@ -58,24 +49,12 @@ export function setContext(key, context) {
* @returns {boolean}
*/
export function hasContext(key) {
return get_or_init_context_map('hasContext').has(key);
return get_or_init_context_map(ssr_context, 'hasContext').has(key);
}
/** @returns {Map<any, any>} */
export function getAllContexts() {
return get_or_init_context_map('getAllContexts');
}
/**
* @param {string} name
* @returns {Map<unknown, unknown>}
*/
function get_or_init_context_map(name) {
if (ssr_context === null) {
e.lifecycle_outside_component(name);
}
return (ssr_context.c ??= new Map(get_parent_context(ssr_context) || undefined));
return get_or_init_context_map(ssr_context, 'getAllContexts');
}
/**
@ -94,24 +73,6 @@ export function pop() {
ssr_context = /** @type {SSRContext} */ (ssr_context).p;
}
/**
* @param {SSRContext} ssr_context
* @returns {Map<unknown, unknown> | null}
*/
function get_parent_context(ssr_context) {
let parent = ssr_context.p;
while (parent !== null) {
const context_map = parent.c;
if (context_map !== null) {
return context_map;
}
parent = parent.p;
}
return null;
}
/**
* Wraps an `await` expression in such a way that the component context that was
* active before the expression evaluated can be reapplied afterwards

@ -0,0 +1,52 @@
import { lifecycle_outside_component, missing_context } from './errors.js';
/**
* @template T
* @param {(key: object) => T} get_context
* @param {(key: object, context: T) => T} set_context
* @param {(key: object) => boolean} has_context
* @returns {[() => T, (context: T) => T]}
*/
export function create_context(get_context, set_context, has_context) {
const key = {};
return [
() => {
if (!has_context(key)) {
missing_context();
}
return get_context(key);
},
(context) => set_context(key, context)
];
}
/**
* @typedef {{ p: Context | null, c: Map<unknown, unknown> | null }} Context
*/
/**
* @param {Context} context
* @returns {Map<unknown, unknown> | null}
*/
function get_parent_context(context) {
let parent = context.p;
while (parent !== null && parent.c === null) {
parent = parent.p;
}
return parent?.c ?? null;
}
/**
* @param {Context | null} context
* @param {string} name
* @returns {Map<unknown, unknown>}
*/
export function get_or_init_context_map(context, name) {
if (context === null) {
lifecycle_outside_component(name);
}
return (context.c ??= new Map(get_parent_context(context) || undefined));
}
Loading…
Cancel
Save