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