chore: tidy up server exports (#10972)

* tidy up server exports

* tidy up server exports

* docs are unnecessary here

* eliminate client dependencies from server code

* lint
pull/10974/head
Rich Harris 5 months ago committed by GitHub
parent d49e2aeb15
commit f303d82043
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -19,6 +19,8 @@ export const TRANSITION_GLOBAL = 1 << 2;
export const TEMPLATE_FRAGMENT = 1;
export const TEMPLATE_USE_IMPORT_NODE = 1 << 1;
export const UNINITIALIZED = Symbol();
/** List of Element events that will be delegated */
export const DelegatedEvents = [
'beforeinput',

@ -1,33 +1,37 @@
import { current_component_context } from './internal/client/runtime.js';
export {
createEventDispatcher,
flushSync,
getAllContexts,
getContext,
hasContext,
mount,
hydrate,
setContext,
tick,
unmount,
untrack
} from './index-client.js';
/** @returns {void} */
export function onMount() {}
import { current_component } from './internal/server/context.js';
import { noop } from './internal/shared/utils.js';
/** @param {() => void} fn */
export function onDestroy(fn) {
const context = /** @type {import('#client').ComponentContext} */ (current_component_context);
(context.ondestroy ??= []).push(fn);
var context = /** @type {import('#server').Component} */ (current_component);
(context.d ??= []).push(fn);
}
export {
noop as beforeUpdate,
noop as afterUpdate,
noop as onMount,
noop as flushSync,
run as untrack
} from './internal/shared/utils.js';
export function createEventDispatcher() {
return noop;
}
/** @returns {void} */
export function beforeUpdate() {}
export function mount() {
throw new Error('mount(...) is not available on the server');
}
export function hydrate() {
throw new Error('hydrate(...) is not available on the server');
}
/** @returns {void} */
export function afterUpdate() {}
export function unmount() {
throw new Error('unmount(...) is not available on the server');
}
export async function tick() {}
/**
* @template T
@ -38,3 +42,5 @@ export function unstate(value) {
// There's no signals/proxies on the server, so just return the value
return value;
}
export { getAllContexts, getContext, hasContext, setContext } from './internal/server/context.js';

@ -13,5 +13,4 @@ export const DESTROYED = 1 << 12;
export const IS_ELSEIF = 1 << 13;
export const EFFECT_RAN = 1 << 14;
export const UNINITIALIZED = Symbol();
export const STATE_SYMBOL = Symbol('$state');

@ -1,4 +1,4 @@
import { is_promise } from '../../../common.js';
import { is_promise } from '../../../shared/utils.js';
import {
current_component_context,
flush_sync,

@ -1,4 +1,4 @@
import { UNINITIALIZED } from '../../constants.js';
import { UNINITIALIZED } from '../../../../constants.js';
import { block, branch, pause_effect } from '../../reactivity/effects.js';
import { safe_not_equal } from '../../reactivity/equality.js';

@ -1,4 +1,4 @@
import { noop } from '../../../common.js';
import { noop } from '../../../shared/utils.js';
import { effect } from '../../reactivity/effects.js';
import { current_effect, untrack } from '../../runtime.js';
import { raf } from '../../timing.js';

@ -1,4 +1,4 @@
import { run_all } from '../../../common.js';
import { run, run_all } from '../../../shared/utils.js';
import { user_pre_effect, user_effect } from '../../reactivity/effects.js';
import {
current_component_context,
@ -9,11 +9,6 @@ import {
untrack
} from '../../runtime.js';
/** @param {Function} fn */
function run(fn) {
return fn();
}
/**
* Legacy-mode only: Call `onMount` callbacks and set up `beforeUpdate`/`afterUpdate` effects
*/

@ -1,4 +1,4 @@
import { run_all } from '../../common.js';
import { run_all } from '../../shared/utils.js';
let is_task_queued = false;
let is_raf_queued = false;

@ -120,15 +120,10 @@ export {
hasContext
} from './runtime.js';
export {
add_snippet_symbol,
validate_component,
validate_dynamic_component,
validate_dynamic_element_tag,
validate_each_keys,
validate_prop_bindings,
validate_snippet,
validate_store,
validate_void_dynamic_element
validate_store
} from './validate.js';
export { raf } from './timing.js';
export { proxy, unstate } from './proxy.js';
@ -140,4 +135,11 @@ export {
$window as window,
$document as document
} from './dom/operations.js';
export { noop } from '../common.js';
export { noop } from '../shared/utils.js';
export {
add_snippet_symbol,
validate_component,
validate_dynamic_element_tag,
validate_snippet,
validate_void_dynamic_element
} from '../shared/validate.js';

@ -13,8 +13,9 @@ import {
} from './utils.js';
import { add_owner, check_ownership, strip_owner } from './dev/ownership.js';
import { mutable_source, source, set } from './reactivity/sources.js';
import { STATE_SYMBOL, UNINITIALIZED } from './constants.js';
import { STATE_SYMBOL } from './constants.js';
import { updating_derived } from './reactivity/deriveds.js';
import { UNINITIALIZED } from '../../constants.js';
/**
* @template T

@ -27,7 +27,7 @@ import {
ROOT_EFFECT
} from '../constants.js';
import { set } from './sources.js';
import { noop } from '../../common.js';
import { noop } from '../../shared/utils.js';
import { remove } from '../dom/reconciler.js';
/**

@ -6,7 +6,6 @@ import {
current_effect,
current_untracked_writes,
current_untracking,
flush_sync,
get,
is_batching_effect,
is_runes,
@ -18,7 +17,8 @@ import {
untrack
} from '../runtime.js';
import { equals, safe_equals } from './equality.js';
import { CLEAN, DERIVED, DIRTY, BRANCH_EFFECT, UNINITIALIZED } from '../constants.js';
import { CLEAN, DERIVED, DIRTY, BRANCH_EFFECT } from '../constants.js';
import { UNINITIALIZED } from '../../../constants.js';
/**
* @template V

@ -1,6 +1,6 @@
import { subscribe_to_store } from '../../../store/utils.js';
import { noop } from '../../common.js';
import { UNINITIALIZED } from '../constants.js';
import { noop } from '../../shared/utils.js';
import { UNINITIALIZED } from '../../../constants.js';
import { get, untrack } from '../runtime.js';
import { effect } from './effects.js';
import { mutable_source, set } from './sources.js';
@ -10,7 +10,7 @@ import { mutable_source, set } from './sources.js';
* signal that will be updated when the store is. The store references container is needed to
* track reassignments to stores and to track the correct component context.
* @template V
* @param {import('#client').Store<V> | null | undefined} store
* @param {import('#shared').Store<V> | null | undefined} store
* @param {string} store_name
* @param {import('#client').StoreReferencesContainer} stores
* @returns {V}
@ -46,7 +46,7 @@ export function store_get(store, store_name, stores) {
* Unsubscribe from a store if it's not the same as the one in the store references container.
* We need this in addition to `store_get` because someone could unsubscribe from a store but
* then never subscribe to the new one (if any), causing the subscription to stay open wrongfully.
* @param {import('#client').Store<any> | null | undefined} store
* @param {import('#shared').Store<any> | null | undefined} store
* @param {string} store_name
* @param {import('#client').StoreReferencesContainer} stores
*/
@ -65,7 +65,7 @@ export function store_unsub(store, store_name, stores) {
/**
* @template V
* @param {import('#client').Store<V> | null | undefined} store
* @param {import('#shared').Store<V> | null | undefined} store
* @param {import('#client').Source<V>} source
*/
function connect_store_to_signal(store, source) {
@ -80,7 +80,7 @@ function connect_store_to_signal(store, source) {
/**
* Sets the new value of a store and returns that value.
* @template V
* @param {import('#client').Store<V>} store
* @param {import('#shared').Store<V>} store
* @param {V} value
* @returns {V}
*/
@ -116,7 +116,7 @@ export function unsubscribe_on_destroy(stores) {
/**
* Updates a store with a new value.
* @param {import('#client').Store<V>} store the store to update
* @param {import('#shared').Store<V>} store the store to update
* @param {any} expression the expression that mutates the store
* @param {V} new_value the new store value
* @template V
@ -127,7 +127,7 @@ export function mutate_store(store, expression, new_value) {
}
/**
* @param {import('#client').Store<number>} store
* @param {import('#shared').Store<number>} store
* @param {number} store_value
* @param {1 | -1} [d]
* @returns {number}
@ -138,7 +138,7 @@ export function update_store(store, store_value, d = 1) {
}
/**
* @param {import('#client').Store<number>} store
* @param {import('#shared').Store<number>} store
* @param {number} store_value
* @param {1 | -1} [d]
* @returns {number}

@ -1055,8 +1055,7 @@ export function push(props, runes = false, fn) {
l1: [],
l2: source(false),
// update_callbacks
u: null,
ondestroy: null
u: null
};
if (DEV) {

@ -1,4 +1,4 @@
import { noop } from '../common.js';
import { noop } from '../shared/utils.js';
const is_client = typeof window !== 'undefined';

@ -1,14 +1,10 @@
import type { Store } from '#shared';
import { STATE_SYMBOL } from './constants.js';
import type { Effect, Source, Value } from './reactivity/types.js';
type EventCallback = (event: Event) => boolean;
export type EventCallbackMap = Record<string, EventCallback | EventCallback[]>;
export type Store<V> = {
subscribe: (run: (value: V) => void) => () => void;
set(value: V): void;
};
// For all the core internal objects, we use single-character property strings.
// This not only reduces code-size and parsing, but it also improves the performance
// when the JS VM JITs the code.
@ -43,8 +39,6 @@ export type ComponentContext = {
/** onMount callbacks */
m: Array<() => any>;
};
// TODO move this to a separate server component context object
ondestroy: null | Array<() => void>;
};
export type Equals = (this: Value, value: unknown) => boolean;

@ -42,27 +42,6 @@ export function validate_dynamic_component(component_fn) {
}
}
/**
* @param {() => string} tag_fn
* @returns {void}
*/
export function validate_void_dynamic_element(tag_fn) {
const tag = tag_fn();
if (tag && is_void(tag)) {
// eslint-disable-next-line no-console
console.warn(`<svelte:element this="${tag}"> is self-closing and cannot have content.`);
}
}
/** @param {() => unknown} tag_fn */
export function validate_dynamic_element_tag(tag_fn) {
const tag = tag_fn();
const is_string = typeof tag === 'string';
if (tag && !is_string) {
throw new Error('<svelte:element> expects "this" attribute to be a string.');
}
}
/**
* @param {() => any} collection
* @param {(item: any, index: number) => string} key_fn
@ -103,41 +82,6 @@ export function loop_guard(timeout) {
};
}
const snippet_symbol = Symbol.for('svelte.snippet');
/**
* @param {any} fn
*/
export function add_snippet_symbol(fn) {
fn[snippet_symbol] = true;
return fn;
}
/**
* Validate that the function handed to `{@render ...}` is a snippet function, and not some other kind of function.
* @param {any} snippet_fn
*/
export function validate_snippet(snippet_fn) {
if (snippet_fn && snippet_fn[snippet_symbol] !== true) {
throw new Error(
'The argument to `{@render ...}` must be a snippet function, not a component or some other kind of function. ' +
'If you want to dynamically render one snippet or another, use `$derived` and pass its result to `{@render ...}`.'
);
}
return snippet_fn;
}
/**
* Validate that the function behind `<Component />` isn't a snippet.
* @param {any} component_fn
*/
export function validate_component(component_fn) {
if (component_fn?.[snippet_symbol] === true) {
throw new Error('A snippet must be rendered with `{@render ...}`');
}
return component_fn;
}
/**
* @param {Record<string, any>} $$props
* @param {string[]} bindable

@ -0,0 +1,84 @@
import { DEV } from 'esm-env';
import { on_destroy } from './index.js';
/** @type {import('#server').Component | null} */
export var current_component = null;
/**
* @template T
* @param {any} key
* @returns {T}
*/
export function getContext(key) {
const context_map = getAllContexts();
const result = /** @type {T} */ (context_map.get(key));
return result;
}
/**
* @template T
* @param {any} key
* @param {T} context
* @returns {T}
*/
export function setContext(key, context) {
getAllContexts().set(key, context);
return context;
}
/**
* @param {any} key
* @returns {boolean}
*/
export function hasContext(key) {
return getAllContexts().has(key);
}
/** @returns {Map<any, any>} */
export function getAllContexts() {
const context = current_component;
if (context === null) {
throw new Error(
'ERR_SVELTE_ORPHAN_CONTEXT' +
(DEV ? 'Context can only be used during component initialisation.' : '')
);
}
return (context.c ??= new Map(get_parent_context(context) || undefined));
}
export function push() {
current_component = { p: current_component, c: null, d: null };
}
export function pop() {
var component = /** @type {import('#server').Component} */ (current_component);
var ondestroy = component.d;
if (ondestroy) {
on_destroy.push(...ondestroy);
}
current_component = component.p;
}
/**
* @param {import('#server').Component} 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,16 +1,14 @@
import * as $ from '../client/runtime.js';
import { is_promise, noop } from '../common.js';
import { is_promise, noop } from '../shared/utils.js';
import { subscribe_to_store } from '../../store/utils.js';
import {
UNINITIALIZED,
DOMBooleanAttributes,
disallowed_paragraph_contents,
interactive_elements,
is_tag_valid_with_parent
} from '../../constants.js';
import { DEV } from 'esm-env';
import { UNINITIALIZED } from '../client/constants.js';
export * from '../client/validate.js';
import { current_component, pop, push } from './context.js';
/**
* @typedef {{
@ -192,14 +190,16 @@ export function render(component, options) {
payload.out += '<![>';
if (options.context) {
$.push({});
/** @type {import('../client/types.js').ComponentContext} */ ($.current_component_context).c =
options.context;
push();
/** @type {import('#server').Component} */ (current_component).c = options.context;
}
component(payload, options.props, {}, {});
if (options.context) {
$.pop();
pop();
}
payload.out += '<!]>';
for (const cleanup of on_destroy) cleanup();
on_destroy = prev_on_destroy;
@ -213,24 +213,6 @@ export function render(component, options) {
};
}
/**
* @param {boolean} runes
*/
export function push(runes) {
$.push({}, runes);
}
export function pop() {
var context = /** @type {import('#client').ComponentContext} */ ($.current_component_context);
var ondestroy = context.ondestroy;
if (ondestroy) {
on_destroy.push(...ondestroy);
}
$.pop();
}
/**
* @template V
* @param {V} value
@ -435,7 +417,7 @@ export function merge_styles(style_attribute, style_directive) {
* @template V
* @param {Record<string, [any, any, any]>} store_values
* @param {string} store_name
* @param {import('../client/types.js').Store<V> | null | undefined} store
* @param {import('#shared').Store<V> | null | undefined} store
* @returns {V}
*/
export function store_get(store_values, store_name, store) {
@ -472,7 +454,7 @@ export function validate_store(store, name) {
/**
* Sets the new value of a store and returns that value.
* @template V
* @param {import('../client/types.js').Store<V>} store
* @param {import('#shared').Store<V>} store
* @param {V} value
* @returns {V}
*/
@ -486,7 +468,7 @@ export function store_set(store, value) {
* @template V
* @param {Record<string, [any, any, any]>} store_values
* @param {string} store_name
* @param {import('../client/types.js').Store<V>} store
* @param {import('#shared').Store<V>} store
* @param {any} expression
*/
export function mutate_store(store_values, store_name, store, expression) {
@ -497,7 +479,7 @@ export function mutate_store(store_values, store_name, store, expression) {
/**
* @param {Record<string, [any, any, any]>} store_values
* @param {string} store_name
* @param {import('../client/types.js').Store<number>} store
* @param {import('#shared').Store<number>} store
* @param {1 | -1} [d]
* @returns {number}
*/
@ -510,7 +492,7 @@ export function update_store(store_values, store_name, store, d = 1) {
/**
* @param {Record<string, [any, any, any]>} store_values
* @param {string} store_name
* @param {import('../client/types.js').Store<number>} store
* @param {import('#shared').Store<number>} store
* @param {1 | -1} [d]
* @returns {number}
*/
@ -672,3 +654,13 @@ export function once(get_value) {
return value;
};
}
export { push, pop } from './context.js';
export {
add_snippet_symbol,
validate_component,
validate_dynamic_element_tag,
validate_snippet,
validate_void_dynamic_element
} from '../shared/validate.js';

@ -0,0 +1,8 @@
export interface Component {
/** parent */
p: null | Component;
/** context */
c: null | Map<unknown, unknown>;
/** ondestroy */
d: null | Array<() => void>;
}

@ -0,0 +1,4 @@
export type Store<V> = {
subscribe: (run: (value: V) => void) => () => void;
set(value: V): void;
};

@ -13,6 +13,11 @@ export function is_promise(value) {
return typeof value?.then === 'function';
}
/** @param {Function} fn */
export function run(fn) {
return fn();
}
/** @param {Array<() => void>} arr */
export function run_all(arr) {
for (var i = 0; i < arr.length; i++) {

@ -0,0 +1,57 @@
import { is_void } from '../../compiler/phases/1-parse/utils/names.js';
const snippet_symbol = Symbol.for('svelte.snippet');
/**
* @param {any} fn
*/
export function add_snippet_symbol(fn) {
fn[snippet_symbol] = true;
return fn;
}
/**
* Validate that the function handed to `{@render ...}` is a snippet function, and not some other kind of function.
* @param {any} snippet_fn
*/
export function validate_snippet(snippet_fn) {
if (snippet_fn && snippet_fn[snippet_symbol] !== true) {
throw new Error(
'The argument to `{@render ...}` must be a snippet function, not a component or some other kind of function. ' +
'If you want to dynamically render one snippet or another, use `$derived` and pass its result to `{@render ...}`.'
);
}
return snippet_fn;
}
/**
* Validate that the function behind `<Component />` isn't a snippet.
* @param {any} component_fn
*/
export function validate_component(component_fn) {
if (component_fn?.[snippet_symbol] === true) {
throw new Error('A snippet must be rendered with `{@render ...}`');
}
return component_fn;
}
/**
* @param {() => string} tag_fn
* @returns {void}
*/
export function validate_void_dynamic_element(tag_fn) {
const tag = tag_fn();
if (tag && is_void(tag)) {
// eslint-disable-next-line no-console
console.warn(`<svelte:element this="${tag}"> is self-closing and cannot have content.`);
}
}
/** @param {() => unknown} tag_fn */
export function validate_dynamic_element_tag(tag_fn) {
const tag = tag_fn();
const is_string = typeof tag === 'string';
if (tag && !is_string) {
throw new Error('<svelte:element> expects "this" attribute to be a string.');
}
}

@ -1,7 +1,7 @@
import { DEV } from 'esm-env';
import { source, set } from '../internal/client/reactivity/sources.js';
import { get } from '../internal/client/runtime.js';
import { UNINITIALIZED } from '../internal/client/constants.js';
import { UNINITIALIZED } from '../constants.js';
import { map } from './utils.js';
/**

@ -1,4 +1,4 @@
import { noop, run_all } from '../internal/common.js';
import { noop, run_all } from '../internal/shared/utils.js';
import { subscribe_to_store } from './utils.js';
/**

@ -1,4 +1,4 @@
import { noop } from '../internal/common.js';
import { noop } from '../internal/shared/utils.js';
/**
* @template T

@ -25,7 +25,9 @@
"svelte/server": ["./src/server/index.js"],
"svelte/store": ["./src/store/public.d.ts"],
"#compiler": ["./src/compiler/types/index.d.ts"],
"#client": ["./src/internal/client/types.d.ts"]
"#client": ["./src/internal/client/types.d.ts"],
"#server": ["./src/internal/server/types.d.ts"],
"#shared": ["./src/internal/shared/types.d.ts"]
}
},
"include": [

Loading…
Cancel
Save