From 9efe5c036422ad5e9341377c985530a160f97ca9 Mon Sep 17 00:00:00 2001 From: pushkine Date: Tue, 14 Apr 2020 20:09:22 +0200 Subject: [PATCH] types, comments --- src/runtime/internal/utils.ts | 23 +++--- src/runtime/store/index.ts | 150 +++++++++++++++++++--------------- 2 files changed, 96 insertions(+), 77 deletions(-) diff --git a/src/runtime/internal/utils.ts b/src/runtime/internal/utils.ts index 487116b655..0cebff6870 100644 --- a/src/runtime/internal/utils.ts +++ b/src/runtime/internal/utils.ts @@ -1,3 +1,5 @@ +import { Store, Unsubscriber } from '../store'; + export function noop() {} export const identity = x => x; @@ -14,7 +16,7 @@ export function is_promise(value: any): value is PromiseLike { export function add_location(element, file, line, column, char) { element.__svelte_meta = { - loc: { file, line, column, char } + loc: { file, line, column, char }, }; } @@ -35,7 +37,7 @@ export function is_function(thing: any): thing is Function { } export function safe_not_equal(a, b) { - return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function'); + return a != a ? b == b : a !== b || (a && typeof a === 'object') || typeof a === 'function'; } export function not_equal(a, b) { @@ -48,17 +50,16 @@ export function validate_store(store, name) { } } -export function subscribe(store, ...callbacks) { - if (store == null) { - return noop; - } +export function subscribe(store: Store, ...callbacks: Parameters['subscribe']>): Unsubscriber { + if (store == null) return noop; const unsub = store.subscribe(...callbacks); + // @ts-ignore return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub; } export function get_store_value(store) { let value; - subscribe(store, _ => value = _)(); + subscribe(store, _ => (value = _))(); return value; } @@ -74,9 +75,7 @@ export function create_slot(definition, ctx, $$scope, fn) { } export function get_slot_context(definition, ctx, $$scope, fn) { - return definition[1] && fn - ? assign($$scope.ctx.slice(), definition[1](fn(ctx))) - : $$scope.ctx; + return definition[1] && fn ? assign($$scope.ctx.slice(), definition[1](fn(ctx))) : $$scope.ctx; } export function get_slot_changes(definition, $$scope, dirty, fn) { @@ -118,7 +117,7 @@ export function compute_rest_props(props, keys) { export function once(fn) { let ran = false; - return function(this: any, ...args) { + return function (this: any, ...args) { if (ran) return; ran = true; fn.call(this, ...args); @@ -138,4 +137,4 @@ export const has_prop = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, export function action_destroyer(action_result) { return action_result && is_function(action_result.destroy) ? action_result.destroy : noop; -} \ No newline at end of file +} diff --git a/src/runtime/store/index.ts b/src/runtime/store/index.ts index 35823fb68b..cafb4e9d6b 100644 --- a/src/runtime/store/index.ts +++ b/src/runtime/store/index.ts @@ -1,4 +1,10 @@ -import { subscribe, noop, safe_not_equal, get_store_value } from 'svelte/internal'; +import { subscribe, noop, safe_not_equal, get_store_value } from '../internal/utils'; + +/** + * Get the current value from a store by subscribing and immediately unsubscribing. + * @param store readable + */ +export { get_store_value as get }; /** Sets the value of a store. */ type Setter = (value: T) => void; @@ -9,15 +15,17 @@ type StopCallback = () => void; * If a callback is returned it will be called on last removed subscriber */ type StartStopNotifier = (set: Setter) => StopCallback | void; type Subscriber = (value: T) => void; -type Unsubscriber = () => void; +export type Unsubscriber = () => void; type Updater = (value: T) => T; -/** Internal callback, invalidates every store before updating */ +/** Internal callback, used to invalidate every store before updating */ type Invalidator = () => void; type SubscribeInvalidateTuple = [Subscriber, Invalidator]; -type Store = Writable | Readable; +export type Store = Writable | Readable; +type ArrayStore = Array>; +type SingleStore = Store; type ValuesOf = { [K in keyof T]: T[K] extends Store ? U : never }; type ValueOf = T extends Store ? U : never; -type StoreValues = T extends Store ? ValueOf : ValuesOf; +type StoreValues = T extends Store ? ValueOf : T extends ArrayStore ? ValuesOf : T; /** The value of the derived store is the value returned by the function */ type AutoDeriver = (values: StoreValues) => T; /** The value of the derived store is set manually through Setter calls */ @@ -106,6 +114,7 @@ export function writable(value: T, start: StartStopNotifier = noop): Writa return { set, update, subscribe }; } + /** * Derived value store by synchronizing one or more readable stores and * applying an aggregation function over its input values. @@ -114,75 +123,86 @@ export function writable(value: T, start: StartStopNotifier = noop): Writa * @param fn - function callback that aggregates the values * @param initial_value - when used asynchronously */ -export function derived | Array>, F extends Deriver, T = DerivedValue>( +export function derived, T = DerivedValue>( stores: S, fn: F, initial_value?: T ) { - const mode = fn.length < 2 ? auto(fn as AutoDeriver) : manual(fn as ManualDeriver); - const deriver = Array.isArray(stores) - ? multiple(stores as Array>, mode) - : single(stores as Store, mode); + const mode: DeriverController = fn.length < 2 ? auto(fn as AutoDeriver) : manual(fn as ManualDeriver); + const deriver = Array.isArray(stores) ? multiple(stores as ArrayStore, mode) : single(stores as SingleStore, mode); return readable(initial_value, deriver) as Readable; } -function single(store: S, controller: DeriverController): StartStopNotifier { - return set => { - const unsub = subscribe(store, value => controller.update(value, set)); - return function stop() { - unsub(), controller.cleanup(); - }; +/** DERIVING LOGIC */ + +/** + * derived from a single store + * + * derived store StartStopNotifier function when given a single store + * */ +const single = (store: SingleStore, controller: DeriverController): StartStopNotifier => set => { + const unsub = subscribe(store, value => controller.update(value, set)); + return function stop() { + unsub(), controller.cleanup(); }; -} -function multiple>, T>(stores: S, controller: DeriverController): StartStopNotifier { - return set => { - let inited = false; - let pending = 0; - const values = new Array(stores.length) as StoreValues; - function sync() { - if (inited && !pending) { - controller.update(values, set); +}; + +/** derived store StartStopNotifier function when given an array of stores */ +const multiple = (stores: ArrayStore, controller: DeriverController): StartStopNotifier => set => { + const values = new Array(stores.length); + + let inited = false; + let pending = 0; + + const sync = () => inited && !pending && controller.update(values, set); + const unsubs = stores.map((store, index) => + subscribe( + store, + value => { + values[index] = value; + pending &= ~(1 << index); + sync(); + }, + () => { + pending |= 1 << index; } - } - const unsubs = stores.map((store, index) => - subscribe( - store, - value => { - values[index] = value; - pending &= ~(1 << index); - sync(); - }, - () => { - pending |= 1 << index; - } - ) - ); - (inited = true), sync(); - return function stop() { - unsubs.forEach(v => v()), controller.cleanup(); - }; - }; -} -function auto(fn): DeriverController { - return { - update(payload, set) { - set(fn(payload)); - }, - cleanup: noop, - }; -} -function manual(fn): DeriverController { - return { - update(payload, set) { - this.cleanup(); - this.cleanup = fn(payload, set) as Unsubscriber; - if (typeof this.cleanup !== 'function') this.cleanup = noop; - }, - cleanup: noop, + ) + ); + + (inited = true), sync(); + return function stop() { + unsubs.forEach(v => v()), controller.cleanup(); }; -} +}; + +/** UPDATE/CLEANUP CONTROLLERS */ + /** - * Get the current value from a store by subscribing and immediately unsubscribing. - * @param store readable + * mode "auto" : function has <2 arguments + * + * derived value = value returned by the function + * */ -export { get_store_value as get }; +const auto = (fn: AutoDeriver): DeriverController => ({ + update(payload, set) { + set(fn(payload)); + }, + cleanup: noop, +}); + +/** + * mode "manual" : function has >1 arguments + * + * derived value = value set manually + * before each update => callback returned by the function + * + * note : [(...args) does not count as an argument] + */ +const manual = (fn: ManualDeriver): DeriverController => ({ + update(payload, set) { + this.cleanup(); + this.cleanup = fn(payload, set) as Unsubscriber; + if (typeof this.cleanup !== 'function') this.cleanup = noop; + }, + cleanup: noop, +});