UNPKG

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
'use strict'; 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