easy-peasy-classes
Version:
easy-peasy enhanced with classes, full typescript type safety, nested store stuctures and model inheritance
493 lines (417 loc) • 16.4 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var tslib = require('tslib');
var easyPeasy = require('easy-peasy');
var lodash = require('lodash');
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _extends() {
_extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
function _inheritsLoose(subClass, superClass) {
subClass.prototype = Object.create(superClass.prototype);
subClass.prototype.constructor = subClass;
_setPrototypeOf(subClass, superClass);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
var MetadataStorage = /*#__PURE__*/function () {
function MetadataStorage() {
this.def = {};
this.properties = [];
this.children = [];
this.actions = [];
this.computed = [];
this.listeners = [];
this.thunks = [];
this.instances = [];
this._isHoldingChildren = false;
}
var _proto = MetadataStorage.prototype;
_proto.addModelMetadata = function addModelMetadata(definition) {
this.def = {
proto: definition.ctor.prototype
};
this.instances.push({
proto: definition.ctor.prototype,
instance: new definition.ctor()
});
};
_proto.addPropertyMetadata = function addPropertyMetadata(definition) {
this.properties.push(definition);
};
_proto.addChildMetadata = function addChildMetadata(definition) {
this.children.push(definition);
};
_proto.addActionMetadata = function addActionMetadata(definition) {
this.actions.push(definition);
};
_proto.addComputedMetadata = function addComputedMetadata(definition) {
this.computed.push(definition);
};
_proto.addListenerMetadata = function addListenerMetadata(definition) {
this.listeners.push(definition);
};
_proto.addThunkMetadata = function addThunkMetadata(definition) {
this.thunks.push(definition);
};
_proto.buildModel = function buildModel() {
var _this = this;
var model = {};
var proto = this.def.proto;
this.properties.filter(function (p) {
return p.proto === proto;
}).forEach(function (p) {
var instance = _this.instances.find(function (i) {
return i.proto === p.proto;
});
var initialValue = instance === null || instance === void 0 ? void 0 : instance.instance[p.fieldName];
model[p.fieldName] = initialValue;
});
this.children.filter(function (p) {
return p.proto === proto;
}).forEach(function (p) {
_this._isHoldingChildren = true;
var instance = _this.instances.find(function (i) {
return i.proto === p.proto;
});
var initialValue = instance === null || instance === void 0 ? void 0 : instance.instance[p.fieldName];
model[p.fieldName] = initialValue;
});
this.actions.filter(function (a) {
return a.proto === proto;
}).forEach(function (a) {
model[a.fieldName] = easyPeasy.action(function (state, payload) {
try {
a.handler.call(state, payload);
} catch (err) {
if (err instanceof TypeError && err.message === "this.getStoreActions is not a function") throw new Error("this.getStoreActions() can only be used in a Thunk.\nIf you used an Action, you should probably add a separate Thunk to do your business logic and keep your actions granular.");
if (err instanceof TypeError && err.message === "this.getStoreState is not a function") throw new Error("this.getStoreState() can only be used in a Thunk.\nIf you used an Action, you should probably add a separate Thunk to do your business logic and keep your actions granular.");
if (err instanceof TypeError && err.message === "this.getStoreStateClone is not a function") throw new Error("this.getStoreStateClone() can only be used in a Thunk.\nIf you used an Action, you should probably add a separate Thunk to do your business logic and keep your actions granular.");
if (err instanceof TypeError && err.message === "this.getStateClone is not a function") throw new Error("this.getStateClone() can only be used in a Thunk.\nIf you used an Action, you should probably add a separate Thunk to do your business logic and keep your actions granular.");else throw err;
}
});
});
this.computed.filter(function (c) {
return c.proto === proto;
}).forEach(function (c) {
model[c.fieldName] = easyPeasy.computed(function (state) {
return c.handler.call(state);
});
});
this.listeners.filter(function (l) {
return l.proto === proto;
}).forEach(function (l) {
model[l.fieldName] = easyPeasy.thunkOn(l.actionFn, function (actions, target, _ref) {
var getState = _ref.getState,
getStoreActions = _ref.getStoreActions,
getStoreState = _ref.getStoreState;
function getStoreStateClone(mapState) {
var selectedState = mapState(getStoreState());
return lodash.cloneDeep(selectedState);
}
function getStateClone() {
var modelState = getState();
return lodash.cloneDeep(modelState);
}
return l.handler.call(_extends({}, lodash.merge({}, actions, getState()), {
getStoreActions: getStoreActions,
getStoreState: getStoreState,
getStateClone: getStateClone,
getStoreStateClone: getStoreStateClone
}), target);
});
});
this.thunks.filter(function (t) {
return t.proto === proto;
}).forEach(function (t) {
model[t.fieldName] = easyPeasy.thunk(function (actions, payload, _ref2) {
var getState = _ref2.getState,
getStoreActions = _ref2.getStoreActions,
getStoreState = _ref2.getStoreState;
//As the state is frozen, it not a danger anymore to provide raw state to Thunks
//The copied unFrozen state is provided as an utility
function getStoreStateClone(mapState) {
var selectedState = mapState(getStoreState());
return lodash.cloneDeep(selectedState);
}
function getStateClone() {
var modelState = getState();
return lodash.cloneDeep(modelState);
}
return t.handler.call(_extends({}, lodash.merge({}, actions, getState()), {
getStoreActions: getStoreActions,
getStoreState: getStoreState,
getStateClone: getStateClone,
getStoreStateClone: getStoreStateClone
}), payload);
});
});
return model;
};
return MetadataStorage;
}();
var MetadataStoragePool = /*#__PURE__*/function () {
function MetadataStoragePool() {
this.pool = [];
this.currentIndex = 0;
this.pool[0] = new MetadataStorage();
}
var _proto2 = MetadataStoragePool.prototype;
_proto2.next = function next() {
this.currentIndex++;
this.pool[this.currentIndex] = new MetadataStorage();
console.log("waiting for new decorators (storageIndex : " + this.currentIndex + ")");
};
_proto2.getCurrentIndex = function getCurrentIndex() {
return this.currentIndex;
};
_proto2.getCurrentStorage = function getCurrentStorage() {
return this.pool[this.currentIndex];
};
_proto2.getStorageByIndex = function getStorageByIndex(index) {
return this.pool[index];
};
return MetadataStoragePool;
}();
var metaDataStoragePool = /*#__PURE__*/new MetadataStoragePool();
//récupère le symbole du storage et l'ajoute a la a la classe parent (celle du décorateur @Model)
var Model = function Model(ctor) {
var metadataStorage = metaDataStoragePool.getCurrentStorage();
metadataStorage.addModelMetadata({
ctor: ctor
});
function getMergedModel() {
var _Object$getPrototypeO;
//get the model def of the class own decorators
var modelObj = metadataStorage.buildModel(); //get the model of its parent (because of inheritance, the parent classes are evaluated first and have a .obj property)
//No need to loop through the prototype chain as the parent itself has looked up for its grand-parent
var parentModelObj = ((_Object$getPrototypeO = Object.getPrototypeOf(ctor)) === null || _Object$getPrototypeO === void 0 ? void 0 : _Object$getPrototypeO.obj) || {}; //eq __proto__
//The child overides the parent (we do not mutate the parent as it may be used concurrently in other models)
var mergedModel = Object.assign({}, parentModelObj, modelObj);
mergedModel._isHoldingChildren = metadataStorage._isHoldingChildren;
return mergedModel;
} //We store the model obj as a static getter (= on the class constructor)
//We don't the result directly, we store a factory function. This way, the action and thunk function will have different refs
Object.defineProperty(ctor, 'obj', {
get: function get() {
return getMergedModel();
}
});
console.log("@Model " + ctor.name);
metaDataStoragePool.next();
};
var Property = function Property(proto, fieldName) {
console.log("@Property ", proto.constructor.name + "." + fieldName.toString());
var metadataStorage = metaDataStoragePool.getCurrentStorage();
metadataStorage.addPropertyMetadata({
proto: proto,
fieldName: fieldName
});
};
var Child = function Child(proto, fieldName) {
console.log("@Child ", proto.constructor.name + "." + fieldName.toString());
var metadataStorage = metaDataStoragePool.getCurrentStorage();
metadataStorage.addChildMetadata({
proto: proto,
fieldName: fieldName
});
};
var Action = function Action(proto, fieldName, descriptor) {
console.log("@Action ", proto.constructor.name + "." + fieldName.toString());
var metadataStorage = metaDataStoragePool.getCurrentStorage();
metadataStorage.addActionMetadata({
proto: proto,
fieldName: fieldName,
handler: descriptor.value
});
};
var Computed = function Computed(proto, fieldName, descriptor) {
console.log("@Computed ", proto.constructor.name + "." + fieldName.toString());
var metadataStorage = metaDataStoragePool.getCurrentStorage();
metadataStorage.addComputedMetadata({
proto: proto,
fieldName: fieldName,
handler: descriptor.value || descriptor.get
});
};
var Thunk = function Thunk(proto, fieldName, descriptor) {
console.log("@Thunk ", proto.constructor.name + "." + fieldName.toString());
var metadataStorage = metaDataStoragePool.getCurrentStorage();
metadataStorage.addThunkMetadata({
proto: proto,
fieldName: fieldName,
handler: descriptor.value
});
};
/**
* equiv thunkOn
*
* @example
* \@Listener<UserTableModel,StoreModel>((actions, storeActions)=> [storeActions.userPage.lstCities.setValue])
* shouldRefresh(target){
* this.resfresh();
* }
*/
function Listener(actionFn) {
return function (proto, fieldName, descriptor) {
console.log("@Listener ", proto.constructor.name + "." + fieldName.toString());
var metadataStorage = metaDataStoragePool.getCurrentStorage();
metadataStorage.addListenerMetadata({
proto: proto,
fieldName: fieldName,
handler: descriptor.value,
actionFn: actionFn
});
};
}
var modelSym = /*#__PURE__*/Symbol('modelSym');
var ModelBasePure = /*#__PURE__*/function () {
function ModelBasePure() {}
_createClass(ModelBasePure, null, [{
key: "obj",
get: function get() {
return {};
}
}]);
return ModelBasePure;
}();
exports.ModelBase = /*#__PURE__*/function (_ModelBasePure) {
_inheritsLoose(ModelBase, _ModelBasePure);
function ModelBase() {
var _this;
_this = _ModelBasePure.apply(this, arguments) || this;
_this._isHoldingChildren = false;
return _this;
}
return ModelBase;
}(ModelBasePure);
tslib.__decorate([Property], exports.ModelBase.prototype, "_isHoldingChildren", void 0);
exports.ModelBase = /*#__PURE__*/tslib.__decorate([Model], exports.ModelBase);
var ModelBasePureTyped = /*#__PURE__*/function (_ModelBasePure2) {
_inheritsLoose(ModelBasePureTyped, _ModelBasePure2);
function ModelBasePureTyped() {
var _this2;
_this2 = _ModelBasePure2.apply(this, arguments) || this;
_this2.getStateClone = undefined;
_this2.getStoreStateClone = undefined;
_this2.getStoreActions = undefined;
_this2.getStoreState = undefined;
return _this2;
}
return ModelBasePureTyped;
}(ModelBasePure);
var ModelBaseTyped = /*#__PURE__*/function (_ModelBase) {
_inheritsLoose(ModelBaseTyped, _ModelBase);
function ModelBaseTyped() {
var _this3;
_this3 = _ModelBase.apply(this, arguments) || this;
_this3.getStateClone = undefined;
_this3.getStoreStateClone = undefined;
_this3.getStoreActions = undefined;
_this3.getStoreState = undefined;
return _this3;
}
return ModelBaseTyped;
}(exports.ModelBase);
//get better typeping for getState() and getActions()
function createStore(storeModel, config) {
var store = easyPeasy.createStore(storeModel, config);
return store;
}
function createTypedHooks() {
var hooks = easyPeasy.createTypedHooks();
function secureUseStoreState(mapState) {
var selectedState = hooks.useStoreState(mapState); //If the selected state is marked as conaining one or more @Child(), the it shouldn't be accessed. The children should be accessed individually
if (selectedState !== null && selectedState !== void 0 && selectedState._isHoldingChildren) {
throw new Error("The selected state located at " + mapState.toString() + " contains one or more @Child() properties and it shouldn't be accessed. Its properties should be accessed individually.");
}
return selectedState;
}
return {
useStoreActions: hooks.useStoreActions,
//useStoreActionsLoose: hooks.useStoreActions,
useStoreState: secureUseStoreState,
//useStoreStateLoose: hooks.useStoreState,
useStoreDispatch: hooks.useStoreDispatch
};
}
function createLocalHooksFactory(hooks) {
return function createLocalHooks(holderMapFn) {
//const hooks = easyPeasy.createTypedHooks<any>();
return {
useLocalActions: function useLocalActions(localMapFn) {
if (localMapFn === void 0) {
localMapFn = function localMapFn(S) {
return S;
};
}
return hooks.useStoreActions(function (store) {
return localMapFn(holderMapFn(store));
});
},
//useLocalActionsLoose: (localMapFn = (S:any)=>S)=> hooks.useStoreActions((store:any) =>localMapFn(holderMapFn(store as ToStoreType<StoreModel>))),
useLocalState: function useLocalState(localMapFn) {
if (localMapFn === void 0) {
localMapFn = function localMapFn(S) {
return S;
};
}
return hooks.useStoreState(function (store) {
return localMapFn(holderMapFn(store));
});
}
};
};
}
function getMapperFactory() {
return {
useMapper: function useMapper(i) {
return i;
}
};
}
exports.Action = Action;
exports.Child = Child;
exports.Computed = Computed;
exports.Listener = Listener;
exports.Model = Model;
exports.ModelBasePure = ModelBasePure;
exports.ModelBasePureTyped = ModelBasePureTyped;
exports.ModelBaseTyped = ModelBaseTyped;
exports.Property = Property;
exports.Thunk = Thunk;
exports.createLocalHooksFactory = createLocalHooksFactory;
exports.createStore = createStore;
exports.createTypedHooks = createTypedHooks;
exports.getMapperFactory = getMapperFactory;
exports.modelSym = modelSym;
//# sourceMappingURL=easy-peasy-classes.cjs.development.js.map