mirror of https://github.com/sveltejs/svelte
commit
ca7aa3cc2e
@ -0,0 +1,26 @@
|
||||
export const SOURCE = 1;
|
||||
export const DERIVED = 1 << 1;
|
||||
export const EFFECT = 1 << 2;
|
||||
export const PRE_EFFECT = 1 << 3;
|
||||
export const RENDER_EFFECT = 1 << 4;
|
||||
export const MANAGED = 1 << 6;
|
||||
export const UNOWNED = 1 << 7;
|
||||
export const CLEAN = 1 << 8;
|
||||
export const DIRTY = 1 << 9;
|
||||
export const MAYBE_DIRTY = 1 << 10;
|
||||
export const INERT = 1 << 11;
|
||||
export const DESTROYED = 1 << 12;
|
||||
|
||||
export const ROOT_BLOCK = 0;
|
||||
export const IF_BLOCK = 1;
|
||||
export const EACH_BLOCK = 2;
|
||||
export const EACH_ITEM_BLOCK = 3;
|
||||
export const AWAIT_BLOCK = 4;
|
||||
export const KEY_BLOCK = 5;
|
||||
export const HEAD_BLOCK = 6;
|
||||
export const DYNAMIC_COMPONENT_BLOCK = 7;
|
||||
export const DYNAMIC_ELEMENT_BLOCK = 8;
|
||||
export const SNIPPET_BLOCK = 9;
|
||||
|
||||
export const UNINITIALIZED = Symbol();
|
||||
export const STATE_SYMBOL = Symbol('$state');
|
@ -0,0 +1,59 @@
|
||||
import { run_all } from '../../common.js';
|
||||
|
||||
let is_task_queued = false;
|
||||
let is_raf_queued = false;
|
||||
|
||||
/** @type {Array<() => void>} */
|
||||
let current_queued_tasks = [];
|
||||
/** @type {Array<() => void>} */
|
||||
let current_raf_tasks = [];
|
||||
|
||||
function process_task() {
|
||||
is_task_queued = false;
|
||||
const tasks = current_queued_tasks.slice();
|
||||
current_queued_tasks = [];
|
||||
run_all(tasks);
|
||||
}
|
||||
|
||||
function process_raf_task() {
|
||||
is_raf_queued = false;
|
||||
const tasks = current_raf_tasks.slice();
|
||||
current_raf_tasks = [];
|
||||
run_all(tasks);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {() => void} fn
|
||||
* @returns {void}
|
||||
*/
|
||||
export function schedule_task(fn) {
|
||||
if (!is_task_queued) {
|
||||
is_task_queued = true;
|
||||
setTimeout(process_task, 0);
|
||||
}
|
||||
current_queued_tasks.push(fn);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {() => void} fn
|
||||
* @returns {void}
|
||||
*/
|
||||
export function schedule_raf_task(fn) {
|
||||
if (!is_raf_queued) {
|
||||
is_raf_queued = true;
|
||||
requestAnimationFrame(process_raf_task);
|
||||
}
|
||||
current_raf_tasks.push(fn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously run any queued tasks.
|
||||
*/
|
||||
export function flush_tasks() {
|
||||
if (is_task_queued) {
|
||||
process_task();
|
||||
}
|
||||
if (is_raf_queued) {
|
||||
process_raf_task();
|
||||
}
|
||||
}
|
@ -0,0 +1,153 @@
|
||||
import { subscribe_to_store } from '../../../store/utils.js';
|
||||
import { noop } from '../../common.js';
|
||||
import { UNINITIALIZED } from '../constants.js';
|
||||
import { get, set, set_ignore_mutation_validation, untrack } from '../runtime.js';
|
||||
import { user_effect } from './computations.js';
|
||||
import { mutable_source } from './sources.js';
|
||||
|
||||
/**
|
||||
* Gets the current value of a store. If the store isn't subscribed to yet, it will create a proxy
|
||||
* 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('../types.js').Store<V> | null | undefined} store
|
||||
* @param {string} store_name
|
||||
* @param {import('../types.js').StoreReferencesContainer} stores
|
||||
* @returns {V}
|
||||
*/
|
||||
export function store_get(store, store_name, stores) {
|
||||
/** @type {import('../types.js').StoreReferencesContainer[''] | undefined} */
|
||||
let entry = stores[store_name];
|
||||
const is_new = entry === undefined;
|
||||
|
||||
if (is_new) {
|
||||
entry = {
|
||||
store: null,
|
||||
last_value: null,
|
||||
value: mutable_source(UNINITIALIZED),
|
||||
unsubscribe: noop
|
||||
};
|
||||
// TODO: can we remove this code? it was refactored out when we split up source/comptued signals
|
||||
// push_destroy_fn(entry.value, () => {
|
||||
// /** @type {import('../types.js').StoreReferencesContainer['']} */ (entry).last_value =
|
||||
// /** @type {import('../types.js').StoreReferencesContainer['']} */ (entry).value.value;
|
||||
// });
|
||||
stores[store_name] = entry;
|
||||
}
|
||||
|
||||
if (is_new || entry.store !== store) {
|
||||
entry.unsubscribe();
|
||||
entry.store = store ?? null;
|
||||
entry.unsubscribe = connect_store_to_signal(store, entry.value);
|
||||
}
|
||||
|
||||
const value = get(entry.value);
|
||||
// This could happen if the store was cleaned up because the component was destroyed and there's a leak on the user side.
|
||||
// In that case we don't want to fail with a cryptic Symbol error, but rather return the last value we got.
|
||||
return value === UNINITIALIZED ? entry.last_value : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template V
|
||||
* @param {import('../types.js').Store<V> | null | undefined} store
|
||||
* @param {import('../types.js').SourceSignal<V>} source
|
||||
*/
|
||||
function connect_store_to_signal(store, source) {
|
||||
if (store == null) {
|
||||
set(source, undefined);
|
||||
return noop;
|
||||
}
|
||||
|
||||
/** @param {V} v */
|
||||
const run = (v) => {
|
||||
set_ignore_mutation_validation(true);
|
||||
set(source, v);
|
||||
set_ignore_mutation_validation(false);
|
||||
};
|
||||
return subscribe_to_store(store, run);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the new value of a store and returns that value.
|
||||
* @template V
|
||||
* @param {import('../types.js').Store<V>} store
|
||||
* @param {V} value
|
||||
* @returns {V}
|
||||
*/
|
||||
export function store_set(store, value) {
|
||||
store.set(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribes from all auto-subscribed stores on destroy
|
||||
* @param {import('../types.js').StoreReferencesContainer} stores
|
||||
*/
|
||||
export function unsubscribe_on_destroy(stores) {
|
||||
on_destroy(() => {
|
||||
let store_name;
|
||||
for (store_name in stores) {
|
||||
const ref = stores[store_name];
|
||||
ref.unsubscribe();
|
||||
// TODO: can we remove this code? it was refactored out when we split up source/comptued signals
|
||||
// destroy_signal(ref.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a store with a new value.
|
||||
* @param {import('../types.js').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
|
||||
*/
|
||||
export function mutate_store(store, expression, new_value) {
|
||||
store.set(new_value);
|
||||
return expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template V
|
||||
* @param {unknown} val
|
||||
* @returns {val is import('../types.js').Store<V>}
|
||||
*/
|
||||
export function is_store(val) {
|
||||
return (
|
||||
typeof val === 'object' &&
|
||||
val !== null &&
|
||||
typeof (/** @type {import('../types.js').Store<V>} */ (val).subscribe) === 'function'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('../types.js').Store<number>} store
|
||||
* @param {number} store_value
|
||||
* @param {1 | -1} [d]
|
||||
* @returns {number}
|
||||
*/
|
||||
export function update_store(store, store_value, d = 1) {
|
||||
store.set(store_value + d);
|
||||
return store_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('../types.js').Store<number>} store
|
||||
* @param {number} store_value
|
||||
* @param {1 | -1} [d]
|
||||
* @returns {number}
|
||||
*/
|
||||
export function update_pre_store(store, store_value, d = 1) {
|
||||
const value = store_value + d;
|
||||
store.set(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a callback to run immediately before the component is unmounted.
|
||||
* @param {() => any} fn
|
||||
* @returns {void}
|
||||
*/
|
||||
function on_destroy(fn) {
|
||||
user_effect(() => () => untrack(fn));
|
||||
}
|
Loading…
Reference in new issue