diff --git a/packages/rax-simulator-renderer/src/obx-rax/derivation.ts b/packages/rax-simulator-renderer/src/obx-rax/derivation.ts deleted file mode 100644 index 58e415cdd..000000000 --- a/packages/rax-simulator-renderer/src/obx-rax/derivation.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { IObservable, IDepTreeNode, addObserver, removeObserver } from './observable/observable'; -import { globalState } from './ global-state'; - -export enum DerivationState { - // before being run or (outside batch and not being observed) - // at this point derivation is not holding any data about dependency tree - NOT_TRACKING = -1, - // no shallow dependency changed since last computation - // won't recalculate derivation - UP_TO_DATE = 0, - // don't have to recompute on every dependency change, but only when it's needed - MYBE_DIRTY = 1, - // A shallow dependency has changed since last computation and the derivation - // will need to recompute when it's needed next. - DIRTY = 2, -} - -export interface IDerivation extends IDepTreeNode { - observing: IObservable[]; - dependenciesState: DerivationState; - newObserving?: null | IObservable[]; - runId?: number; // Id of the current run of a derivation. - unboundDepsCount?: number; // amount of dependencies used by the derivation in this run, which has not been bound yet. - onBecomeDirty(): void; -} - -export class CaughtException { - constructor(public cause: any) { - // Empty - } -} - -export function isCaughtException(e: any): e is CaughtException { - return e instanceof CaughtException; -} - -interface ModifiedValue { - ifModified(): void; -} - -export function isModifiedValue(v: any): v is ModifiedValue { - return v.ifModified ? true : false; -} - -export function shouldCompute(derivation: IDerivation): boolean { - switch (derivation.dependenciesState) { - case DerivationState.UP_TO_DATE: - return false; - case DerivationState.NOT_TRACKING: - case DerivationState.DIRTY: - return true; - case DerivationState.MYBE_DIRTY: { - const prevUntracked = untrackedStart(); - const obs = derivation.observing; - const l = obs.length; - for (let i = 0; i < l; i++) { - const obj = obs[i]; - if (isModifiedValue(obj)) { - obj.ifModified(); - } - - if ((derivation.dependenciesState as any) === DerivationState.DIRTY) { - untrackedEnd(prevUntracked); - return true; - } - } - changeDependenciesStateTo0(derivation); - untrackedEnd(prevUntracked); - return false; - } - } -} - -export function runDerivedFunction(derivation: IDerivation, f: (...args: any[]) => any, context?: any) { - const prevTracking = globalState.trackingDerivation; - // pre allocate array allocation + room for variation in deps - derivation.newObserving = new Array(derivation.observing.length + 100); - derivation.unboundDepsCount = 0; - derivation.runId = ++globalState.runId; - globalState.trackingDerivation = derivation; - let result; - try { - result = f.call(context); - } catch (e) { - result = new CaughtException(e); - } - globalState.trackingDerivation = prevTracking; - changeDependenciesStateTo0(derivation); - bindDependencies(derivation); - return result; -} - -function bindDependencies(derivation: IDerivation) { - const prevObserving = derivation.observing; - const observing = (derivation.observing = derivation.newObserving!); - let lowestNewObservingDerivationState = DerivationState.UP_TO_DATE; - - // Go through all new observables and check diffValue: (this list can contain duplicates): - // 0: first occurrence, change to 1 and keep it - // 1: extra occurrence, drop it - let i0 = 0; - let l = derivation.unboundDepsCount!; - for (let i = 0; i < l; i++) { - const dep = observing[i]; - if (!dep.diffFlag) { - dep.diffFlag = true; - if (i0 !== i) { - observing[i0] = dep; - } - i0++; - } - - // Upcast is 'safe' here, because if dep is IObservable, `dependenciesState` will be undefined, - // not hitting the condition - if (((dep as any) as IDerivation).dependenciesState > lowestNewObservingDerivationState) { - lowestNewObservingDerivationState = ((dep as any) as IDerivation).dependenciesState; - } - } - observing.length = i0; - - derivation.newObserving = null; - // Go through all old observables and check diffValue: (it is unique after last bindDependencies) - // 0: it's not in new observables, unobserve it - // 1: it keeps being observed, don't want to notify it. change to 0 - l = prevObserving.length; - while (l--) { - const dep = prevObserving[l]; - if (!dep.diffFlag) { - removeObserver(dep, derivation); - } - dep.diffFlag = false; - } - - // Go through all new observables and check diffValue: (now it should be unique) - // 0: it was set to 0 in last loop. don't need to do anything. - // 1: it wasn't observed, let's observe it. set back to 0 - while (i0--) { - const dep = observing[i0]; - if (dep.diffFlag) { - dep.diffFlag = false; - addObserver(dep, derivation); - } - } - - // Some new observed derivations may become stale during this derivation computation - // so they have had no chance to propagate staleness (#916) - if (lowestNewObservingDerivationState !== DerivationState.UP_TO_DATE) { - derivation.dependenciesState = lowestNewObservingDerivationState; - derivation.onBecomeDirty(); - } -} - -export function clearObserving(derivation: IDerivation) { - const obs = derivation.observing; - derivation.observing = []; - let i = obs.length; - while (i--) { - removeObserver(obs[i], derivation); - } - - derivation.dependenciesState = DerivationState.NOT_TRACKING; -} - -export function untracked(action: () => T): T { - const prev = untrackedStart(); - const res = action(); - untrackedEnd(prev); - return res; -} - -export function untrackedStart(): IDerivation | null { - const prev = globalState.trackingDerivation; - globalState.trackingDerivation = null; - return prev; -} - -export function untrackedEnd(prev: IDerivation | null) { - globalState.trackingDerivation = prev; -} - -export function changeDependenciesStateTo0(derivation: IDerivation) { - if (derivation.dependenciesState === DerivationState.UP_TO_DATE) { - return; - } - derivation.dependenciesState = DerivationState.UP_TO_DATE; - - const obs = derivation.observing; - let i = obs.length; - while (i--) { - obs[i].lowestObserverState = DerivationState.UP_TO_DATE; - } -} - -export function setDerivationDirty(derivation: IDerivation) { - if (derivation.dependenciesState === DerivationState.UP_TO_DATE) { - derivation.onBecomeDirty(); - } - derivation.dependenciesState = DerivationState.DIRTY; -} - -export function setDerivationMybeDirty(derivation: IDerivation) { - if (derivation.dependenciesState === DerivationState.UP_TO_DATE) { - derivation.onBecomeDirty(); - } - derivation.dependenciesState = DerivationState.MYBE_DIRTY; -} - -export function resetDerivationState(derivation: IDerivation) { - derivation.dependenciesState = DerivationState.NOT_TRACKING; -} diff --git a/packages/rax-simulator-renderer/src/obx-rax/global-state.ts b/packages/rax-simulator-renderer/src/obx-rax/global-state.ts deleted file mode 100644 index 7b6542172..000000000 --- a/packages/rax-simulator-renderer/src/obx-rax/global-state.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { IDerivation } from './derivation'; -import { Reaction } from './reaction'; -import { IObservable } from './observable/observable'; - -export class Globals { - /** - * Currently running derivation - */ - trackingDerivation: IDerivation | null = null; - - /** - * Are we running a computation currently? (not a reaction) - */ - computationDepth = 0; - - /** - * Each time a derivation is tracked, it is assigned a unique run-id - */ - runId = 0; - - /** - * 'guid' for general purpose. Will be persisted amongst resets. - */ - guid = 0; - - /** - * Are we in a batch block? (and how many of them) - */ - inBatch = 0; - - /** - * Observables that don't have observers anymore - */ - pendingUnobservations: IObservable[] = []; - - /** - * List of scheduled, not yet executed, reactions. - */ - pendingReactions: Reaction[] = []; - - /** - * Are we currently processing reactions? - */ - isRunningReactions = false; - - /** - * disable dynamic observe - */ - dynamicObserveDisabled = false; - - reset() { - this.trackingDerivation = null; - this.computationDepth = 0; - this.runId = 0; - this.guid = 0; - this.inBatch = 0; - this.pendingUnobservations = []; - this.pendingReactions = []; - this.isRunningReactions = false; - this.dynamicObserveDisabled = false; - } -} - -export const globalState: Globals = new Globals(); - -export function getGlobalState(): Globals { - return globalState; -} diff --git a/packages/rax-simulator-renderer/src/obx-rax/next-tick.ts b/packages/rax-simulator-renderer/src/obx-rax/next-tick.ts deleted file mode 100644 index a5a2f4af9..000000000 --- a/packages/rax-simulator-renderer/src/obx-rax/next-tick.ts +++ /dev/null @@ -1,56 +0,0 @@ -const callbacks: Array<() => void> = []; -let pending = false; - -function flush() { - pending = false; - const copies = callbacks.slice(0); - callbacks.length = 0; - for (const fn of copies) { - fn(); - } -} - -let timerFlush: () => void; -if (typeof process === 'object' && process.nextTick) { - timerFlush = () => process.nextTick(flush); -} else if (typeof Promise === 'function') { - // tslint:disable-line - const timer = Promise.resolve(); // tslint:disable-line - timerFlush = () => { - timer.then(flush); - // if (isIOS) setTimeout(noop) - }; -} else if (typeof MessageChannel === 'function') { - const channel = new MessageChannel(); - const port = channel.port2; - channel.port1.onmessage = flush; - timerFlush = () => { - port.postMessage(1); - }; -} else { - timerFlush = () => { - setTimeout(flush, 0); - }; -} - -export function clearTicks() { - callbacks.length = 0; -} - -export function nextTick(callback?: () => void): Promise { - let _resovle: () => void; - - callbacks.push(() => { - callback && callback(); - _resovle(); - }); - - if (!pending) { - pending = true; - timerFlush(); - } - - return new Promise((resolve) => { - _resovle = resolve; - }); -} diff --git a/packages/rax-simulator-renderer/src/obx-rax/observable/compare.ts b/packages/rax-simulator-renderer/src/obx-rax/observable/compare.ts deleted file mode 100644 index 1b4481a89..000000000 --- a/packages/rax-simulator-renderer/src/obx-rax/observable/compare.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { getProxiedValue } from './proxy'; - -export function is(a: any, b: any) { - return getProxiedValue(a) === getProxiedValue(b); -} diff --git a/packages/rax-simulator-renderer/src/obx-rax/observable/observable.ts b/packages/rax-simulator-renderer/src/obx-rax/observable/observable.ts deleted file mode 100644 index 951f15f4c..000000000 --- a/packages/rax-simulator-renderer/src/obx-rax/observable/observable.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { isObject } from 'lodash/isObject'; -import { nextId } from '../utils'; -import { DerivationState, IDerivation, setDerivationDirty } from '../derivation'; -import { globalState } from '../ global-state'; -import Obx, { hasObx, getObx, injectObx, ObxFlag } from './obx'; - -export interface IDepTreeNode { - id: string; - name: string; - observing: IObservable[]; -} - -export interface IObservable extends IDepTreeNode { - diffFlag?: boolean; - - observers: Set; - - // Used to avoid redundant propagations - lowestObserverState: DerivationState; - // Used to push itself to global.pendingUnobservations at most once per batch. - isPendingUnobservation?: boolean; - - // Id of the derivation *run* that last accessed this observable. - lastAccessedBy?: number; - isBeingObserved?: boolean; - onBecomeUnobserved(): void; -} - -export function addObserver(observable: IObservable, node: IDerivation) { - observable.observers.add(node); - - if (observable.lowestObserverState > node.dependenciesState) { - observable.lowestObserverState = node.dependenciesState; - } -} - -export function removeObserver(observable: IObservable, node: IDerivation) { - observable.observers.delete(node); - if (observable.observers.size === 0) { - // deleting last observer - queueForUnobservation(observable); - } -} - -export function queueForUnobservation(observable: IObservable) { - if (!observable.isPendingUnobservation) { - observable.isPendingUnobservation = true; - globalState.pendingUnobservations.push(observable); - } -} - -export function startBatch() { - globalState.inBatch++; -} - -export function endBatch() { - if (--globalState.inBatch === 0) { - // the batch is actually about to finish, all unobserving should happen here. - const list = globalState.pendingUnobservations; - for (let i = 0; i < list.length; i++) { - const observable = list[i]; - observable.isPendingUnobservation = false; - if (observable.observers.size === 0) { - if (observable.isBeingObserved) { - // if this observable had reactive observers, trigger the hooks - observable.isBeingObserved = false; - observable.onBecomeUnobserved(); - } - } - } - globalState.pendingUnobservations = []; - } -} - -export function reportObserved(observable: IObservable): void { - const derivation = globalState.trackingDerivation; - if (!derivation) { - return; - } - - if (derivation.runId !== observable.lastAccessedBy) { - observable.lastAccessedBy = derivation.runId; - derivation.newObserving![derivation.unboundDepsCount!++] = observable; - if (!observable.isBeingObserved) { - observable.isBeingObserved = true; - } - } -} - -export function propagateChanged(observable: IObservable, force = false) { - if (observable.lowestObserverState === DerivationState.DIRTY && !force) { - return; - } - observable.lowestObserverState = DerivationState.DIRTY; - observable.observers.forEach((d) => setDerivationDirty(d)); -} - -export function propagateChangeConfirmed(observable: IObservable) { - if (observable.lowestObserverState === DerivationState.DIRTY) { - return; - } - observable.lowestObserverState = DerivationState.DIRTY; - - observable.observers.forEach((d) => { - if (d.dependenciesState === DerivationState.MYBE_DIRTY) { - d.dependenciesState = DerivationState.DIRTY; - } else if (d.dependenciesState === DerivationState.UP_TO_DATE) { - observable.lowestObserverState = DerivationState.UP_TO_DATE; - } - }); -} - -export function propagateMaybeChanged(observable: IObservable) { - if (observable.lowestObserverState !== DerivationState.UP_TO_DATE) { - return; - } - observable.lowestObserverState = DerivationState.MYBE_DIRTY; - - observable.observers.forEach((d) => { - if (d.dependenciesState === DerivationState.UP_TO_DATE) { - d.dependenciesState = DerivationState.MYBE_DIRTY; - d.onBecomeDirty(); - } - }); -} - -export function asObservable(thing: any, obxFlag: ObxFlag): Obx | undefined { - if (!isObject(thing)) { - return; - } - - if (hasObx(thing)) { - return getObx(thing); - } - - if (!Object.isExtensible(thing)) { - return; - } - - const name = (thing.constructor.name || 'ObservableObject') + '@' + nextId(); - const ObxContructor = (asObservable as any).getObxContructor(thing); - const obx = ObxContructor ? new ObxContructor(name, thing, obxFlag) : null; - - if (obx) { - injectObx(thing, obx); - return obx; - } -} - -(asObservable as any).getObxContructor = () => Obx; - -export function observeIterable(items: Iterable, obxFlag: ObxFlag): void { - for (const n of items) { - asObservable(n, obxFlag); - } -} - -export function reportPropValue(propValue: any, propFlag: ObxFlag): void { - if (propValue == null) return; - - const x = propFlag > ObxFlag.REF ? asObservable(propValue, propFlag) : getObx(propValue); - - if (x) { - reportObserved(x); - } -} - -export function reportChildValue(propValue: any, ownerFlag: ObxFlag): void { - if (propValue == null) return; - - const x = - ownerFlag > ObxFlag.VAL - ? asObservable(propValue, ownerFlag === ObxFlag.DEEP ? ObxFlag.DEEP : ObxFlag.VAL) - : getObx(propValue); - - if (x) { - reportObserved(x); - } -} diff --git a/packages/rax-simulator-renderer/src/obx-rax/observable/obx-array.ts b/packages/rax-simulator-renderer/src/obx-rax/observable/obx-array.ts deleted file mode 100644 index 14641de65..000000000 --- a/packages/rax-simulator-renderer/src/obx-rax/observable/obx-array.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { addHiddenProp } from '../utils'; -import { observeIterable, reportChildValue } from './observable'; -import { supportProxy, isProxied, createProxy, SYMBOL_PROXY, SYMBOL_RAW, getProxiedValue, getRawValue } from './proxy'; -import Obx, { getObx, SYMBOL_OBX, ObxFlag } from './obx'; -import { setPrototypeOf } from '../../utils/set-prototype-of'; - -export function childFlag(flag: ObxFlag) { - return flag === ObxFlag.DEEP ? ObxFlag.DEEP : ObxFlag.VAL; -} - -function isValidArrayIndex(val: any, limit: number = -1): boolean { - const n = parseFloat(String(val)); - return n >= 0 && Math.floor(n) === n && isFinite(val) && (limit < 0 || n < limit); -} - -export default class ObxArray extends Obx { - constructor(name: string, target: any[], obxFlag: ObxFlag = ObxFlag.DEEP) { - super(name, target, obxFlag); - - if (supportProxy) { - this.target = createProxy(target, arrayProxyTraps); - } else if (obxFlag > ObxFlag.VAL) { - observeIterable(target, childFlag(obxFlag)); - } - setPrototypeOf(target, arrayMethods); - } - - set(key: PropertyKey, val: any) { - const target = this.target; - if (isValidArrayIndex(key)) { - const index = Number(key); - target.length = Math.max(target.length, index); - target.splice(index, 1, val); - return; - } - super.set(key, val); - } - - del(key: PropertyKey) { - if (isValidArrayIndex(key, this.target.length)) { - this.target.splice(Number(key), 1); - return; - } - super.del(key); - } -} - -// ======= patches ======== -const arrayProto = Array.prototype; -const arrayMethods = Object.create(arrayProto); - -['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(method => { - const original = (arrayProto as any)[method]; - addHiddenProp(arrayMethods, method, function mutator(this: any[], ...args: any[]) { - const obx = getObx(this) as ObxArray; - const proxied = isProxied(this); - const length = this.length; - // apply to rawTarget avoid to call Proxy.set - const result = original.apply(getRawValue(this), args); - - let changed = true; - let inserted; - switch (method) { - case 'push': - case 'unshift': - inserted = args; - changed = inserted.length > 0; - break; - case 'splice': - inserted = args.slice(2); - changed = inserted.length > 0 || this.length !== length; - break; - case 'pop': - case 'shift': - changed = this.length !== length; - break; - } - if (!proxied && obx.obxFlag > ObxFlag.VAL) { - if (inserted && inserted.length > 0) { - observeIterable(inserted, childFlag(obx.obxFlag)); - } - } - - if (obx && changed) { - obx.reportChange(); - } - return result; - }); -}); - -const arrayProxyTraps: ProxyHandler = { - has(rawTarget, name: PropertyKey) { - if (name === SYMBOL_OBX || name === SYMBOL_RAW) { - return true; - } - return name in rawTarget; - }, - get(rawTarget, name: PropertyKey) { - if (name === SYMBOL_RAW) { - return rawTarget; - } - if (name === SYMBOL_OBX || name === SYMBOL_PROXY || name in arrayMethods) { - return (rawTarget as any)[name]; - } - - if (isValidArrayIndex(name)) { - const v = rawTarget[Number(name)]; - const obx = getObx(rawTarget); - if (obx) { - reportChildValue(v, obx.obxFlag); - } - return getProxiedValue(v); - } - - return getProxiedValue((rawTarget as any)[name]); - }, - set(rawTarget, name: PropertyKey, value: any) { - if (name === 'length') { - rawTarget[name] = value; - return true; - } - if (name === SYMBOL_OBX || name === SYMBOL_PROXY || name in arrayMethods) { - return false; - } - - if (isValidArrayIndex(name)) { - const index = Number(name); - rawTarget.length = Math.max(rawTarget.length, index); - rawTarget.splice(index, 1, value); - return true; - } - - (rawTarget as any)[name] = value; - return true; - }, - deleteProperty(rawTarget, name: PropertyKey) { - if (name === SYMBOL_OBX || name === SYMBOL_PROXY || name in arrayMethods) { - return false; - } - - if (isValidArrayIndex(name)) { - rawTarget.splice(Number(name), 1); - return true; - } - - delete (rawTarget as any)[name]; - return true; - }, - preventExtensions() { - return false; - }, -}; diff --git a/packages/rax-simulator-renderer/src/obx-rax/observable/obx-instance.ts b/packages/rax-simulator-renderer/src/obx-rax/observable/obx-instance.ts deleted file mode 100644 index bc5c867fc..000000000 --- a/packages/rax-simulator-renderer/src/obx-rax/observable/obx-instance.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { DecoratorTarget } from '../decorators'; -import Obx from './obx'; - -export default class ObxInstance extends Obx { - set(key: PropertyKey, val: any) { - const target = this.target; - if (key in target) { - (target as any)[key] = val; - return; - } - - super.set(key, val); - } -} diff --git a/packages/rax-simulator-renderer/src/obx-rax/observable/obx-map.ts b/packages/rax-simulator-renderer/src/obx-rax/observable/obx-map.ts deleted file mode 100644 index edd1d5deb..000000000 --- a/packages/rax-simulator-renderer/src/obx-rax/observable/obx-map.ts +++ /dev/null @@ -1,37 +0,0 @@ -import Obx, { ObxFlag } from './obx'; -import { patchMutator, patchAccessor } from './obx-set'; -import { setPrototypeOf } from '../../utils/set-prototype-of'; - -type MapType = Map; - -export default class ObxMap extends Obx { - constructor(name: string, target: MapType, obxFlag: ObxFlag = ObxFlag.DEEP) { - super(name, target, obxFlag); - - setPrototypeOf(target, mapMethods); - } - - has(key: PropertyKey) { - return this.target.has(key); - } - - set(key: PropertyKey, val: any) { - this.target.set(key, val); - } - - get(key: PropertyKey) { - return this.target.get(key); - } - - del(key: PropertyKey) { - this.target.delete(key); - } -} - -// ======= Map ======== -const mapProto = Map.prototype; -const mapMethods = Object.create(mapProto); - -patchMutator(['set', 'clear', 'delete'], mapProto, mapMethods); - -patchAccessor(['values', 'entries', Symbol.iterator, 'forEach', 'get'], mapProto, mapMethods); diff --git a/packages/rax-simulator-renderer/src/obx-rax/observable/obx-object.ts b/packages/rax-simulator-renderer/src/obx-rax/observable/obx-object.ts deleted file mode 100644 index 1ea79ed84..000000000 --- a/packages/rax-simulator-renderer/src/obx-rax/observable/obx-object.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { walk } from '../utils'; -import { supportProxy, createProxy, getProxiedValue, SYMBOL_PROXY, SYMBOL_RAW } from './proxy'; -import Obx, { getObx, SYMBOL_OBX, ObxFlag } from './obx'; -import { defineObxProperty, ensureObxProperty } from './obx-property'; -import { hasOwnProperty } from '../../utils/has-own-property'; - -function propFlag(flag: ObxFlag) { - return flag === ObxFlag.DEEP ? ObxFlag.DEEP : flag - 1; -} - -export default class ObxObject extends Obx { - constructor(name: string, target: object, obxFlag: ObxFlag = ObxFlag.DEEP) { - super(name, target, obxFlag); - - if (supportProxy) { - this.target = createProxy(target, objectProxyTraps); - } else if (obxFlag > ObxFlag.REF) { - walk(target as any, (obj, key, val) => { - defineObxProperty(obj, key, val, undefined, propFlag(obxFlag)); - }); - } - } - - set(key: PropertyKey, val: any) { - const target = this.target; - if (key in target && !(key in objectProto)) { - (target as any)[key] = val; - return; - } - - super.set(key, val); - } -} - -const objectProto = Object.prototype; - -const objectProxyTraps: ProxyHandler = { - has(rawTarget, name: PropertyKey) { - if (name === SYMBOL_OBX || name === SYMBOL_RAW) { - return true; - } - return name in rawTarget; - }, - get(rawTarget, name: PropertyKey) { - if (name === SYMBOL_RAW) { - return rawTarget; - } - if (name === SYMBOL_OBX || name === SYMBOL_PROXY || name in objectProto) { - return rawTarget[name]; - } - - if (hasOwnProperty(rawTarget, name)) { - const obx = getObx(rawTarget); - if (obx) { - ensureObxProperty(rawTarget, name, propFlag(obx.obxFlag)); - } - } - - return getProxiedValue(rawTarget[name]); - }, - set(rawTarget, name: PropertyKey, value: any) { - if (name === SYMBOL_OBX || name === SYMBOL_PROXY || name in objectProto) { - return false; - } - - if (!hasOwnProperty(rawTarget, name)) { - const obx = getObx(rawTarget); - if (obx) { - defineObxProperty(rawTarget, name, value, undefined, propFlag(obx.obxFlag)); - obx.reportChange(); - return true; - } - } - - rawTarget[name] = value; - return true; - }, - deleteProperty(rawTarget, name: PropertyKey) { - if (name === SYMBOL_OBX || name === SYMBOL_PROXY || !hasOwnProperty(rawTarget, name)) { - return false; - } - - delete rawTarget[name]; - const obx = getObx(rawTarget); - if (obx) { - obx.reportChange(); - } - return true; - }, - preventExtensions() { - return false; - }, -}; diff --git a/packages/rax-simulator-renderer/src/obx-rax/observable/obx-property.ts b/packages/rax-simulator-renderer/src/obx-rax/observable/obx-property.ts deleted file mode 100644 index 2920f5268..000000000 --- a/packages/rax-simulator-renderer/src/obx-rax/observable/obx-property.ts +++ /dev/null @@ -1,212 +0,0 @@ -import { globalState } from '../ global-state'; -import { - untrackedStart, - untrackedEnd, - IDerivation, - DerivationState, - runDerivedFunction, - shouldCompute, - isCaughtException, - clearObserving, - setDerivationDirty, -} from '../derivation'; -import { - IObservable, - reportObserved, - startBatch, - endBatch, - propagateChangeConfirmed, - propagateMaybeChanged, - reportPropValue, -} from './observable'; -import { nextId } from '../utils'; -import { ObxFlag, SYMBOL_OBX, getObx } from './obx'; -import { getProxiedValue } from './proxy'; -import { is } from './compare'; -import { isPrimitive } from '../utils/is-primitive'; -import { invariant } from '../utils/invariant'; -import { hasOwnProperty } from '../utils/has-own-property'; - -function getVer(obj: any): number { - const obx = getObx(obj); - return obx ? obx.localVer : 0; -} - -export function asNewValue(obj: object) { - const obx = getObx(obj); - if (obx) { - obx.localVer = obx.localVer + 1; - } - return obj; -} - -const DIRTY = Symbol('dirty'); - -export default class ObxProperty implements IObservable, IDerivation { - id = nextId(); - observing: IObservable[] = []; - observers = new Set(); - dependenciesState = DerivationState.NOT_TRACKING; - lowestObserverState = DerivationState.UP_TO_DATE; - - private isComputing = false; - private isRunningSetter = false; - private pending = false; - private pendingValue: any = null; - private objectVer = 0; - - constructor( - public name: string, - public scope: object | null, - private getter?: (...rest: any[]) => any, - private setter?: (v: any) => void, - private value?: any, - private obxFlag: ObxFlag = ObxFlag.DEEP, - ) {} - - onBecomeDirty() { - propagateMaybeChanged(this); - } - - onBecomeUnobserved() { - clearObserving(this); - } - - ifModified() { - if (this.getter && shouldCompute(this)) { - startBatch(); - if (this.computeValue()) { - propagateChangeConfirmed(this); - this.objectVer = getVer(this.value); - } - endBatch(); - } else if (this.pending) { - this.pending = false; - const oldValue = this.value; - this.value = this.pendingValue; - if (!this.is(oldValue, this.value)) { - propagateChangeConfirmed(this); - this.objectVer = getVer(this.value); - } - } - } - - private is(oldValue: any, value: any) { - return oldValue !== DIRTY && is(oldValue, value) && (isPrimitive(value) || getVer(value) === this.objectVer); - } - - get() { - invariant(!this.isComputing, `Cycle detected in computation ${this.name}`, this.getter); - - reportObserved(this); - - this.ifModified(); - const result = this.value!; - - if (isCaughtException(result)) { - throw result.cause; - } - - reportPropValue(result, this.obxFlag); - - return getProxiedValue(result); - } - - set(value: any) { - invariant(!this.isRunningSetter, `The setter of observable value '${this.name}' is trying to update itself.`); - - invariant(Boolean(this.setter || !this.getter), `Cannot assign a new value to readonly value '${this.name}'.`); - - const oldValue = this.pending ? this.pendingValue : this.value; - - if (!isCaughtException(oldValue) && this.is(oldValue, value)) { - return; - } - - if (!this.setter) { - this.pendingValue = value; - if (!this.pending) { - this.pending = true; - propagateMaybeChanged(this); - } - } else { - this.isRunningSetter = true; - const prevTracking = untrackedStart(); - try { - this.value = DIRTY; - this.setter!.call(this.scope, value); - } finally { - untrackedEnd(prevTracking); - } - this.isRunningSetter = false; - setDerivationDirty(this); - } - } - - private computeValue(): boolean { - const oldValue = this.value; - this.isComputing = true; - globalState.computationDepth++; - this.value = runDerivedFunction(this, this.getter!, this.scope); - globalState.computationDepth--; - this.isComputing = false; - return isCaughtException(oldValue) || isCaughtException(this.value) || !this.is(oldValue, this.value); - } -} - -function isObxProperty(descriptor?: PropertyDescriptor) { - if (!descriptor || !descriptor.get) { - return false; - } - return (descriptor.get as any)[SYMBOL_OBX] ? true : false; -} - -export function ensureObxProperty(obj: any, prop: PropertyKey, obxFlag: ObxFlag = ObxFlag.DEEP) { - const descriptor = Object.getOwnPropertyDescriptor(obj, prop); - if (!descriptor || isObxProperty(descriptor)) { - return; - } - defineObxProperty(obj, prop, undefined, descriptor, obxFlag); -} - -export function defineObxProperty( - obj: object, - key: PropertyKey, - val: any, - descriptor?: PropertyDescriptor, - obxFlag: ObxFlag = ObxFlag.DEEP, -): void { - if (!descriptor) { - descriptor = Object.getOwnPropertyDescriptor(obj, key); - } - if (descriptor && descriptor.configurable === false) { - return; - } - - if (val == null && descriptor && hasOwnProperty(descriptor, 'value')) { - val = descriptor.value; - } - - const getter = descriptor && descriptor.get; - const setter = descriptor && descriptor.set; - const property = new ObxProperty(String(key), obj, getter, setter, val, obxFlag); - const get = () => property.get(); - (get as any)[SYMBOL_OBX] = property; - - Object.defineProperty(obj, key, { - enumerable: true, - configurable: true, - get, - set: (newVal) => property.set(newVal), - }); -} - -export function getObxProperty(obj: object, key: PropertyKey) { - const descriptor = Object.getOwnPropertyDescriptor(obj, key); - - if (!descriptor || !descriptor.get) { - return null; - } - - return (descriptor.get as any)[SYMBOL_OBX] as ObxProperty; -} diff --git a/packages/rax-simulator-renderer/src/obx-rax/observable/obx-set.ts b/packages/rax-simulator-renderer/src/obx-rax/observable/obx-set.ts deleted file mode 100644 index ca9821f31..000000000 --- a/packages/rax-simulator-renderer/src/obx-rax/observable/obx-set.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { addHiddenProp } from '../utils'; -import Obx, { getObx, ObxFlag } from './obx'; -import { reportChildValue } from './observable'; -import { getProxiedValue } from './proxy'; -import { setPrototypeOf } from '../../utils/set-prototype-of'; - -type SetType = Set | WeakSet; - -export default class ObxSet extends Obx { - constructor(name: string, target: SetType, obxFlag: ObxFlag = ObxFlag.DEEP) { - super(name, target, obxFlag); - - setPrototypeOf(target, target instanceof Set ? setMethods : weaksetMethods); - } - - has(key: PropertyKey) { - return false; - } - - set(key: PropertyKey, val: any) {} - - get(key: PropertyKey) { - return undefined; - } - - del(key: PropertyKey) {} -} - -function isIterator(v: any): v is Iterator { - return v.next ? true : false; -} - -export function patchAccessor(keys: Array, proto: any, methods: object): void { - keys.forEach(method => { - const original = proto[method]; - addHiddenProp(methods, method, function accessor(this: any, ...args: any[]) { - const obx = getObx(this); - const flag = obx ? obx.obxFlag : ObxFlag.REF; - if (method === 'forEach') { - const fn = args[0]; - const thisArg = args[0] || this; - args[0] = (v: any, a: any, c: any) => { - reportChildValue(v, flag); - return fn.call(thisArg, getProxiedValue(v), a, c); - }; - return original.apply(this, args); - } - - const result = original.apply(this, args); - - if (method === 'get') { - reportChildValue(result, flag); - return getProxiedValue(result); - } - - if (isIterator(result)) { - const originNext = result.next; - const isMapIter = String(result) === '[object Map Iterator]'; - const isEntries = method === 'entries'; - let keys: string[] | null = null; - if (isEntries && !isMapIter) { - keys = ['0', '1']; - } else if (isMapIter && (isEntries || method === Symbol.iterator)) { - keys = ['1']; - } - - result.next = function next() { - let n = originNext.call(this); - if (!n.done) { - if (keys) { - n.value = createResultProxy(n.value, flag, keys); - } else { - n = createResultProxy(n, flag); - } - } - return n; - }; - } - - return result; - }); - }); -} - -function createResultProxy(entries: any, flag: ObxFlag, keys: any[] = ['value']) { - return new Proxy(entries, { - get(target, key) { - const v = target[key]; - if (v && keys.includes(key)) { - reportChildValue(v, flag); - } - return getProxiedValue(v); - }, - }); -} - -export function patchMutator(keys: Array, proto: any, methods: object): void { - keys.forEach(method => { - const original = proto[method]; - addHiddenProp(methods, method, function mutator(this: any, ...args: any[]) { - const size = this.size; - const result = original.apply(this, args); - const obx = getObx(this); - let changed = true; - switch (method) { - case 'add': - case 'clear': - case 'delete': - changed = this.size !== size; - break; - } - // now: "set" not compare values, default changed - if (obx && changed) { - obx.reportChange(); - } - return result; - }); - }); -} - -// ======= Set ======== -const setProto = Set.prototype; -const setMethods = Object.create(setProto); - -patchMutator(['add', 'clear', 'delete'], setProto, setMethods); - -patchAccessor(['values', 'keys', 'entries', 'forEach', Symbol.iterator], setProto, setMethods); - -// ======= WeakSet ======== -const weaksetProto = WeakSet.prototype; -const weaksetMethods = Object.create(weaksetProto); - -patchMutator(['add', 'delete', 'clear'], weaksetProto, weaksetMethods); diff --git a/packages/rax-simulator-renderer/src/obx-rax/observable/obx.ts b/packages/rax-simulator-renderer/src/obx-rax/observable/obx.ts deleted file mode 100644 index ec5477d53..000000000 --- a/packages/rax-simulator-renderer/src/obx-rax/observable/obx.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { walk, addHiddenFinalProp, nextId } from '../utils'; -import { defineObxProperty } from './obx-property'; -import { IObservable, propagateChanged, startBatch, endBatch } from './observable'; -import { IDerivation, DerivationState, clearObserving } from '../derivation'; -import { hasOwnProperty } from '../utils/has-own-property'; -import { splitPath } from '../utils/split-path'; - -export enum ObxFlag { - REF = 0, - VAL = 1, - SHALLOW = 2, - DEEP = 3, -} - -class Obx implements IObservable, IDerivation { - id = nextId(); - localVer = 0; - observing: IObservable[] = []; - observers = new Set(); - dependenciesState = DerivationState.NOT_TRACKING; - lowestObserverState = DerivationState.UP_TO_DATE; - - constructor(public name: string, public target: T, public obxFlag: ObxFlag = ObxFlag.DEEP) {} - - onBecomeDirty() { - propagateChanged(this); - } - - onBecomeUnobserved() { - clearObserving(this); - } - - reportChange(force = false) { - startBatch(); - this.localVer++; - propagateChanged(this, force); - endBatch(); - } - - // TODO: remove this unused function, move to utils $getAsObx - getAsObx(path: PropertyKey): Obx | void { - if (path === '') { - return this; - } - - let entry = path; - let nestPath = ''; - - if (typeof path === 'string') { - const pathArray = splitPath(path); - - if (!pathArray) { - return this; - } - - entry = pathArray[1]; - nestPath = pathArray[2]; - } - - if (!entry) { - return this.get(nestPath); - } - - let ret = this.get(entry); - - if (!hasObx(ret) && nestPath) { - ret = this.get(path); - } - - const obx = getObx(ret); - - if (obx && nestPath) { - return obx.getAsObx(nestPath); - } - - return obx; - } - - has(key: PropertyKey): boolean { - if (key == null || key === '') { - return false; - } - - return key in this.target; - } - - get(key?: PropertyKey): any { - if (key == null || key === '') { - return this.target; - } - - return (this.target as any)[key]; - } - - set(key: PropertyKey, val: any): void { - if (this.obxFlag > ObxFlag.REF) { - defineObxProperty(this.target as any, key, val, undefined, this.obxFlag); - } else { - (this.target as any)[key] = val; - } - - this.reportChange(); - } - - del(key: PropertyKey) { - if (!hasOwnProperty(this.target, key)) { - return; - } - - delete (this.target as any)[key]; - this.reportChange(); - } - - extend(properties: object) { - startBatch(); - walk(properties, (_, key, val) => this.set(key, val)); - endBatch(); - } -} - -export default Obx; - -export const SYMBOL_OBX = Symbol.for('obx'); -export function injectObx(obj: any[] | object, obx: Obx): void { - addHiddenFinalProp(obj, SYMBOL_OBX, obx); -} - -export function getObx(obj: any): Obx | undefined { - return obj ? (obj as any)[SYMBOL_OBX] : undefined; -} - -export function hasObx(obj: any[] | object): boolean { - return hasOwnProperty(obj, SYMBOL_OBX) && (obj as any)[SYMBOL_OBX] instanceof Obx; -} - -export function reportChange(obj: any, force = false) { - const obx = getObx(obj); - if (obx) { - obx.reportChange(force); - } -} diff --git a/packages/rax-simulator-renderer/src/obx-rax/observable/proxy.ts b/packages/rax-simulator-renderer/src/obx-rax/observable/proxy.ts deleted file mode 100644 index 4d5b17773..000000000 --- a/packages/rax-simulator-renderer/src/obx-rax/observable/proxy.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { addHiddenFinalProp } from '../utils'; - -export const SYMBOL_PROXY = Symbol.for('proxy'); -export const SYMBOL_RAW = Symbol.for('raw'); -export interface Proxied { - [SYMBOL_PROXY]: T; -} - -export function isProxied(target: T): target is T & Proxied { - return SYMBOL_PROXY in target; -} - -export function getProxy(target: T & Proxied) { - return target[SYMBOL_PROXY]; -} - -export function setProxy(target: object, proxy: object) { - addHiddenFinalProp(target, SYMBOL_PROXY, proxy); -} - -export function getProxiedValue(target: any) { - return (target && getProxy(target)) || target; -} - -export function getRawValue(target: any) { - return (target && target[SYMBOL_RAW]) || target; -} - -export const supportProxy = 'Proxy' in global; - -export function createProxy(target: T, taps: ProxyHandler) { - if (isProxied(target)) { - return getProxy(target); - } - - const proxy = new Proxy(target, taps); - setProxy(target, proxy); - - return proxy; -} diff --git a/packages/rax-simulator-renderer/src/obx-rax/observer.ts b/packages/rax-simulator-renderer/src/obx-rax/observer.ts deleted file mode 100644 index 345b9896b..000000000 --- a/packages/rax-simulator-renderer/src/obx-rax/observer.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { FunctionComponent, ComponentType } from 'react'; -import { Component } from 'rax'; -import { Reaction } from './reaction'; -import { shallowEqual } from './utils'; - -const SYMBOL_REACTION = Symbol('__obxReaction'); -const SYMBOL_ISUNMOUNTED = Symbol('__obxIsUnmounted'); - -/** - * ReactiveMixin - */ -function defaultComponentWillUnmount(this: any) { - this.render[SYMBOL_REACTION] && this.render[SYMBOL_REACTION].sleep(); - this[SYMBOL_ISUNMOUNTED] = true; -} - -function defaultShouldComponentUpdate(this: any, nextProps: any, nextState: any) { - if (this.state !== nextState) { - return true; - } - return !shallowEqual(this.props, nextProps); -} - -// function shouldConstruct(C: any) { -// const prototype = C.prototype; -// return !!(prototype && prototype.isReactComponent); -// } - -function shouldConstruct(C: any) { - const prototype = C.prototype; - return !!(prototype && prototype.constructor); -} - -function isFunctionComponent(type: Function): type is FunctionComponent { - return !shouldConstruct(type); -} - -export function getReaction(target: Component): Reaction | undefined { - return (target.render as any)[SYMBOL_REACTION]; -} - -/** - * Observer function / decorator - */ -export function observer>(target: T): T { - if (!target) { - throw new Error('Please pass a valid component to "observer"'); - } - if (typeof target !== 'function') { - throw new Error('obx observer: needs to be a react class constructor or stateless function components'); - } - - let componentClass: any = target; - - if (isFunctionComponent(target)) { - componentClass = class extends Component { - static displayName = componentClass.displayName || componentClass.name; - static contextTypes = componentClass.contextTypes; - static propTypes = componentClass.propTypes; - static defaultProps = componentClass.defaultProps; - - render() { - return target.call(this, this.props, this.context); - } - }; - } - - const proto = componentClass.prototype || componentClass; - mixinLifecycleEvents(proto); - componentClass.isObxReactObserver = true; - const baseRender = proto.render; - proto.render = function() { - return makeComponentReactive.call(this, baseRender); - }; - return componentClass; -} - -function makeComponentReactive(this: any, render: any) { - function reactiveRender() { - isRenderingPending = false; - let exception = undefined; - let rendering = undefined; - reaction.track(() => { - try { - rendering = baseRender(); - } catch (e) { - exception = e; - } - }); - if (exception) { - throw exception; - } - return rendering || baseRender(); - } - - // Generate friendly name for debugging - const initialName = - this.displayName || - this.name || - (this.constructor && (this.constructor.displayName || this.constructor.name)) || - ''; - - const rootNodeID = (this._reactInternalFiber && this._reactInternalFiber._debugID) || '*'; - - // wire up reactive render - const baseRender = render.bind(this); - let isRenderingPending = false; - const reaction = new Reaction( - `${initialName}#${rootNodeID}.render()`, - () => { - if (!isRenderingPending) { - isRenderingPending = true; - if (typeof this.componentWillReact === 'function') { - this.componentWillReact(); - } - if (this[SYMBOL_ISUNMOUNTED] !== true) { - let hasError = true; - try { - Component.prototype.forceUpdate.call(this); - hasError = false; - } finally { - if (hasError) reaction.sleep(); - } - } - } - }, - this.$level || 0, - ); - - (reactiveRender as any)[SYMBOL_REACTION] = reaction; - this.render = reactiveRender; - return reactiveRender.call(this); -} - -function mixinLifecycleEvents(target: any) { - if (!target.componentWillUnmount) { - target.componentWillUnmount = defaultComponentWillUnmount; - } else { - const originFunc = target.componentWillUnmount; - target.componentWillUnmount = function(this: any) { - originFunc.call(this); - defaultComponentWillUnmount.call(this); - }; - } - - if (!target.shouldComponentUpdate) { - target.shouldComponentUpdate = defaultShouldComponentUpdate; - } -} diff --git a/packages/rax-simulator-renderer/src/obx-rax/reaction.ts b/packages/rax-simulator-renderer/src/obx-rax/reaction.ts deleted file mode 100644 index 54c7ed113..000000000 --- a/packages/rax-simulator-renderer/src/obx-rax/reaction.ts +++ /dev/null @@ -1,252 +0,0 @@ -import { - DerivationState, - IDerivation, - runDerivedFunction, - isCaughtException, - shouldCompute, - clearObserving, - CaughtException, -} from './derivation'; -import { nextTick } from './next-tick'; -import { IObservable, endBatch, startBatch } from './observable/observable'; -import { globalState } from './ global-state'; -import { throttle } from './utils/throttle'; - -export function nextId() { - return (++globalState.guid).toString(36).toLocaleLowerCase(); -} - -export class Reaction implements IDerivation { - observing: IObservable[] = []; - dependenciesState = DerivationState.NOT_TRACKING; - id = nextId(); - scheduled = false; - run: () => void; - caughtException: any = null; - - private sleeping = false; - private running = false; - - constructor(public name: string, private check: () => void, public level: number = 0, throttleWait = 10) { - if (throttleWait > 0) { - this.run = throttle(this.runReaction.bind(this), throttleWait); - } else { - this.run = this.runReaction.bind(this); - } - } - - onBecomeDirty() { - this.schedule(); - } - - schedule() { - if (this.scheduled || this.sleeping) { - return; - } - this.scheduled = true; - scheduleReaction(this); - } - - isDirty(): boolean { - return shouldCompute(this); - } - - runReaction() { - if (this.sleeping || this.running) { - return; - } - - startBatch(); - if (shouldCompute(this)) { - this.caughtException = null; - try { - this.check(); - } catch (e) { - this.caughtException = new CaughtException(e); - } - } - endBatch(); - } - - track(fn: () => void) { - startBatch(); - this.running = true; - const result = runDerivedFunction(this, fn); - if (isCaughtException(result)) { - this.caughtException = result; - } - this.running = false; - if (this.sleeping) { - clearObserving(this); - } - endBatch(); - } - - sleep() { - if (this.sleeping) { - return; - } - this.sleeping = true; - if (!this.running) { - startBatch(); - clearObserving(this); - endBatch(); - deScheduleReaction(this); - } - } - - wakeup(sync = false) { - if (this.sleeping) { - this.sleeping = false; - if (sync) { - this.runReaction(); - } else { - this.schedule(); - } - } - } -} - -let flushIndex = 0; -let flushWaiting = false; - -function scheduleReaction(reaction: Reaction) { - const { pendingReactions, isRunningReactions } = globalState; - if (!isRunningReactions) { - pendingReactions.push(reaction); - } else { - let i = pendingReactions.length - 1; - // 0 1 2 3 4 5 6 7 8 9 - // 0, 0, 1, 1, 1, 2, 2, 2, 3, 3 - // ^ ^ - // flushIndex = 2 level = 2 - // break at: i = 7 or i = 2 - while (i > flushIndex && pendingReactions[i].level > reaction.level) { - i--; - } - pendingReactions.splice(i + 1, 0, reaction); - } - - runReactions(); -} - -function deScheduleReaction(reaction: Reaction) { - const { pendingReactions, isRunningReactions } = globalState; - if (!isRunningReactions) { - const i = pendingReactions.indexOf(reaction); - if (i > -1) { - pendingReactions.splice(i, 1); - } - } -} - -export function runReactions() { - // queue the flush - if (!flushWaiting) { - flushWaiting = true; - nextTick(flushReactions); - } -} - -const MAX_REACTION_ITERATIONS = 100; - -function flushReactions() { - globalState.isRunningReactions = true; - const allReactions = globalState.pendingReactions; - let pendingLength = 0; - let iterations = 0; - - // low level run first - // sort as: - // 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 5 - allReactions.sort((a, b) => a.level - b.level); - - while (allReactions.length > pendingLength) { - pendingLength = allReactions.length; - if (++iterations === MAX_REACTION_ITERATIONS) { - // tslint:disable-next-line - console.error( - `Reaction doesn't converge to a stable state after ${MAX_REACTION_ITERATIONS} iterations.` + - ` Probably there is a cycle in the reactive function: ${allReactions[0]}`, - ); - break; - } - for (; flushIndex < pendingLength; flushIndex++) { - allReactions[flushIndex].scheduled = false; - allReactions[flushIndex].run(); - } - } - - flushIndex = 0; - flushWaiting = false; - allReactions.length = 0; - globalState.isRunningReactions = false; -} - -export function clearReactions() { - const { pendingReactions } = globalState; - let i = pendingReactions.length; - while (i--) { - pendingReactions[i].sleep(); - } -} - -export interface Disposer { - (): void; - name?: string; - $obx: Reaction; -} -export interface AutorunOptions { - throttle?: number; - sync?: boolean; - level?: number; - name?: string; - runFirstNow?: boolean; -} -export interface RunContext { - dispose: Disposer; - firstRun: boolean; -} -export type Action = (this: RunContext, context: RunContext) => any; - -export function autorun(action: Action, options: number | true | AutorunOptions = {}): Disposer { - if (typeof options === 'number') { - options = { throttle: options }; - } else if (options === true) { - options = { sync: true }; - } - const name: string = options.name || (action as any).name || 'Autorun@' + nextId(); - - const reaction = new Reaction( - name, - function(this: Reaction) { - this.track(reactionRunner); - }, - options.level || 0, - options.throttle || 0, - ); - - const dispose = () => { - reaction.sleep(); - }; - - dispose.$obx = reaction; - - let firstRun = true; - function reactionRunner() { - const ctx: RunContext = { - firstRun, - dispose, - }; - action.call(ctx, ctx); - firstRun = false; - } - - if (options.sync || options.runFirstNow) { - reaction.runReaction(); - } else { - reaction.schedule(); - } - - return dispose; -} diff --git a/packages/rax-simulator-renderer/src/obx-rax/utils.ts b/packages/rax-simulator-renderer/src/obx-rax/utils.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/rax-simulator-renderer/src/obx-rax/utils/has-own-property.ts b/packages/rax-simulator-renderer/src/obx-rax/utils/has-own-property.ts deleted file mode 100644 index f0b34e5d7..000000000 --- a/packages/rax-simulator-renderer/src/obx-rax/utils/has-own-property.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const prototypeHasOwnProperty = Object.prototype.hasOwnProperty; - -export function hasOwnProperty(obj: any, key: string | number | symbol): boolean { - return obj && prototypeHasOwnProperty.call(obj, key); -} diff --git a/packages/rax-simulator-renderer/src/obx-rax/utils/index.ts b/packages/rax-simulator-renderer/src/obx-rax/utils/index.ts deleted file mode 100644 index bdb320735..000000000 --- a/packages/rax-simulator-renderer/src/obx-rax/utils/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export * from './shallow-equal'; -export * from './has-own-property'; -export * from './throttle'; -export * from './next-id'; -export * from './is-primitive'; -export * from './invariant'; -export * from './splitPath'; diff --git a/packages/rax-simulator-renderer/src/obx-rax/utils/invariant.ts b/packages/rax-simulator-renderer/src/obx-rax/utils/invariant.ts deleted file mode 100644 index 09780d00f..000000000 --- a/packages/rax-simulator-renderer/src/obx-rax/utils/invariant.ts +++ /dev/null @@ -1,5 +0,0 @@ -export function invariant(check: any, message: string, thing?: any) { - if (!check) { - throw new Error('[recore] Invariant failed: ' + message + (thing ? ` in '${thing}'` : '')); - } -} diff --git a/packages/rax-simulator-renderer/src/obx-rax/utils/is-primitive.ts b/packages/rax-simulator-renderer/src/obx-rax/utils/is-primitive.ts deleted file mode 100644 index 8e4b685a7..000000000 --- a/packages/rax-simulator-renderer/src/obx-rax/utils/is-primitive.ts +++ /dev/null @@ -1,8 +0,0 @@ -export function isPrimitive(obj: any): boolean { - // null | undefined - if (obj == null) { - return true; - } - const t = typeof obj; - return t === 'boolean' || t === 'number' || t === 'string' || t === 'symbol'; -} diff --git a/packages/rax-simulator-renderer/src/obx-rax/utils/next-id.ts b/packages/rax-simulator-renderer/src/obx-rax/utils/next-id.ts deleted file mode 100644 index 9ab6b836a..000000000 --- a/packages/rax-simulator-renderer/src/obx-rax/utils/next-id.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { globalState } from '../ global-state'; -export function nextId() { - return (++globalState.guid).toString(36).toLocaleLowerCase(); -} diff --git a/packages/rax-simulator-renderer/src/obx-rax/utils/shallow-equal.ts b/packages/rax-simulator-renderer/src/obx-rax/utils/shallow-equal.ts deleted file mode 100644 index 6dc85c1c2..000000000 --- a/packages/rax-simulator-renderer/src/obx-rax/utils/shallow-equal.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { prototypeHasOwnProperty } from './has-own-property'; - -export function shallowEqual(objA: any, objB: any): boolean { - if (objA === objB) { - return true; - } - - if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) { - return false; - } - - const keysA = Object.keys(objA); - const keysB = Object.keys(objB); - - if (keysA.length !== keysB.length) { - return false; - } - - // Test for A's keys different from B. - const bHasOwnProperty = prototypeHasOwnProperty.bind(objB); - for (let i = 0; i < keysA.length; i++) { - if (!bHasOwnProperty(keysA[i]) || objA[keysA[i]] !== objB[keysA[i]]) { - return false; - } - } - - return true; -} diff --git a/packages/rax-simulator-renderer/src/obx-rax/utils/split-path.ts b/packages/rax-simulator-renderer/src/obx-rax/utils/split-path.ts deleted file mode 100644 index a4c5b5018..000000000 --- a/packages/rax-simulator-renderer/src/obx-rax/utils/split-path.ts +++ /dev/null @@ -1,5 +0,0 @@ -const RE_PATH = /^([^/]*)(?:\/(.*))?$/; -const RE_PATH_REVERSE = /^(?:(.*)\/)?([^/]+)$/; -export function splitPath(path: string, reverse = false) { - return reverse ? RE_PATH_REVERSE.exec(path) : RE_PATH.exec(path); -} diff --git a/packages/rax-simulator-renderer/src/obx-rax/utils/throttle.ts b/packages/rax-simulator-renderer/src/obx-rax/utils/throttle.ts deleted file mode 100644 index ac486f03e..000000000 --- a/packages/rax-simulator-renderer/src/obx-rax/utils/throttle.ts +++ /dev/null @@ -1,97 +0,0 @@ -const useRAF = typeof requestAnimationFrame === 'function'; - -export function throttle(func: Function, wait: number) { - let lastArgs: any; - let lastThis: any; - let result: any; - let timerId: number | undefined; - let lastCalled: number | undefined; - let lastInvoked = 0; - - function invoke(time: number) { - const args = lastArgs; - const thisArg = lastThis; - - lastArgs = undefined; - lastThis = undefined; - lastInvoked = time; - result = func.apply(thisArg, args); - return result; - } - - function startTimer(pendingFunc: any, wait: number): number { - if (useRAF) { - return requestAnimationFrame(pendingFunc); - } - return setTimeout(pendingFunc, wait) as any; - } - - function leadingEdge(time: number) { - lastInvoked = time; - timerId = startTimer(timerExpired, wait); - return invoke(time); - } - - function shouldInvoke(time: number) { - const timeSinceLastCalled = time - lastCalled!; - const timeSinceLastInvoked = time - lastInvoked; - - return ( - lastCalled === undefined || timeSinceLastCalled >= wait || timeSinceLastCalled < 0 || timeSinceLastInvoked >= wait - ); - } - - function remainingWait(time: number) { - const timeSinceLastCalled = time - lastCalled!; - const timeSinceLastInvoked = time - lastInvoked; - - return Math.min(wait - timeSinceLastCalled, wait - timeSinceLastInvoked); - } - - function timerExpired() { - const time = Date.now(); - if (shouldInvoke(time)) { - return trailingEdge(time); - } - - timerId = startTimer(timerExpired, remainingWait(time)); - } - - function trailingEdge(time: number) { - timerId = undefined; - - if (lastArgs) { - return invoke(time); - } - - lastArgs = undefined; - lastThis = undefined; - return result; - } - - function debounced(this: any, ...args: any[]) { - const time = Date.now(); - const isInvoking = shouldInvoke(time); - - lastArgs = args; - lastThis = this; - lastCalled = time; - - if (isInvoking) { - if (timerId === undefined) { - return leadingEdge(lastCalled); - } - - timerId = startTimer(timerExpired, wait); - return invoke(lastCalled); - } - - if (timerId === undefined) { - timerId = startTimer(timerExpired, wait); - } - - return result; - } - - return debounced; -}