@difizen/mana-observable
Version:
225 lines (224 loc) • 7.19 kB
JavaScript
/* eslint-disable @typescript-eslint/no-explicit-any */
import { getPropertyDescriptor, isPlainObject } from '@difizen/mana-common';
import { ObservableSymbol } from "./core";
import { Notifiable } from "./notifiable";
import { Notifier } from "./notifier";
import { observable } from "./observable";
import { Observability } from "./utils";
export function tryInvokeGetter(getter, proxy, origin) {
try {
return getter.call(proxy);
} catch (e) {
if (e instanceof TypeError && e.message.includes('Illegal invocation')) {
return getter.call(origin);
}
throw e;
}
}
export var Trackable;
(function (_Trackable) {
var activator = _Trackable.activator = Symbol('activator');
function is(target) {
return Observability.canBeObservable(target) && target[ObservableSymbol.Tracker];
}
_Trackable.is = is;
function getOrigin(target) {
return target[ObservableSymbol.Tracker];
}
_Trackable.getOrigin = getOrigin;
function tryGetOrigin(target) {
if (!is(target)) {
return target;
}
return getOrigin(target);
}
_Trackable.tryGetOrigin = tryGetOrigin;
})(Trackable || (Trackable = {}));
export var Tracker;
(function (_Tracker) {
function set(target, act, proxy) {
Reflect.defineMetadata(act, proxy, target, ObservableSymbol.Tracker);
}
_Tracker.set = set;
function get(target, act) {
return Reflect.getMetadata(act, target, ObservableSymbol.Tracker);
}
_Tracker.get = get;
function has(target, act) {
return Reflect.hasOwnMetadata(act, target, ObservableSymbol.Tracker);
}
_Tracker.has = has;
function tramsform(toTrack, act, activator) {
if (toTrack instanceof Array) {
return transformArray(toTrack, act, activator);
}
if (toTrack instanceof Map) {
return transformMap(toTrack, act, activator);
}
if (isPlainObject(toTrack)) {
return transformPlainObject(toTrack, act, activator);
}
return toTrack;
}
_Tracker.tramsform = tramsform;
function transformArray(toTrack, act, activator) {
return new Proxy(toTrack, {
get: function get(target, property) {
var value = target[property];
if (property === ObservableSymbol.Self) {
return value;
}
if (Object.isFrozen(target)) {
return value;
}
if (property === Trackable.activator) {
return activator;
}
return track(value, act);
}
});
}
_Tracker.transformArray = transformArray;
function transformPlainObject(toTrack, act, activator) {
return new Proxy(toTrack, {
get: function get(target, property) {
var value = target[property];
if (property === ObservableSymbol.Self) {
return value;
}
if (Object.isFrozen(target)) {
return value;
}
if (property === Trackable.activator) {
return activator;
}
return track(value, act);
}
});
}
_Tracker.transformPlainObject = transformPlainObject;
function transformMap(toTrack, act, activator) {
return new Proxy(toTrack, {
get: function get(target, property) {
var value = target[property];
if (property === ObservableSymbol.Self) {
return value;
}
if (Object.isFrozen(target)) {
return value;
}
if (property === Trackable.activator) {
return activator;
}
if (property === 'get' && typeof value === 'function') {
return function () {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
var innerValue = value.apply(target, args);
return track(innerValue, act);
};
}
return value;
}
});
}
_Tracker.transformMap = transformMap;
function toInstanceTracker(origin, act) {
// try make observable
if (!Observability.marked(origin)) {
observable(origin);
}
var proxy = new Proxy(origin, {
get: function get(target, property) {
if (property === ObservableSymbol.Tracker) {
return target;
}
if (property === ObservableSymbol.Self) {
return target;
}
var notifier;
var value;
if (typeof property === 'string') {
if (Observability.shouldKeepOrigin(target, property)) {
return target[property];
}
var descriptor = getPropertyDescriptor(target, property);
if ((descriptor === null || descriptor === void 0 ? void 0 : descriptor.configurable) === false && (descriptor === null || descriptor === void 0 ? void 0 : descriptor.writable) === false) {
// non-configurable and non-writable property should return the actual value
return target[property];
}
if (Observability.marked(target, property)) {
notifier = Notifier.getOrCreate(target, property);
Notifier.once(notifier, act, function () {
act({
key: property,
value: target[property]
});
});
value = target[property];
return track(value, act, true);
}
if (descriptor !== null && descriptor !== void 0 && descriptor.get) {
return tryInvokeGetter(descriptor.get, proxy, target);
}
}
value = target[property];
return track(value, act, true);
}
});
set(origin, act, proxy);
return proxy;
}
_Tracker.toInstanceTracker = toInstanceTracker;
function toNotifiableTracker(origin, act, asProperty) {
var maybeNotifiable = Notifiable.get(origin);
if (!maybeNotifiable) {
maybeNotifiable = Notifiable.transform(origin);
}
// set reactable listener
var activator;
if (!asProperty && Notifiable.is(maybeNotifiable)) {
var notifier = Notifiable.getNotifier(maybeNotifiable);
activator = function activator() {
Notifier.once(notifier, act, function () {
act(origin);
});
};
activator();
}
var proxy = tramsform(maybeNotifiable, act, activator);
set(origin, act, proxy);
return proxy;
}
_Tracker.toNotifiableTracker = toNotifiableTracker;
function track(object, act) {
var asProperty = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
if (!Observability.canBeObservable(object)) {
return object;
}
// get origin
var origin = object;
if (Trackable.is(object)) {
origin = Trackable.getOrigin(object);
}
origin = Observability.getOrigin(origin);
// already has tracker
var exist = undefined;
if (has(origin, act)) {
exist = get(origin, act);
if (exist) {
if (exist[Trackable.activator]) {
exist[Trackable.activator]();
}
return exist;
}
}
if (Notifiable.canBeNotifiable(origin)) {
return toNotifiableTracker(origin, act, asProperty);
} else {
return toInstanceTracker(origin, act);
}
}
_Tracker.track = track;
})(Tracker || (Tracker = {}));