mmlpx
Version:
mobx model layer paradigm
173 lines (160 loc) • 6.08 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
var _pull2 = require('lodash/pull');
var _pull3 = _interopRequireDefault(_pull2);
var _mergeWith2 = require('lodash/mergeWith');
var _mergeWith3 = _interopRequireDefault(_mergeWith2);
exports.applySnapshot = applySnapshot;
exports.patchSnapshot = patchSnapshot;
exports.getSnapshot = getSnapshot;
exports.onSnapshot = onSnapshot;
var _mobx = require('mobx');
var _instantiate = require('../core/dependency-inject/instantiate');
var _types = require('../utils/types');
var _genReactiveInjector = require('./genReactiveInjector');
var _genReactiveInjector2 = _interopRequireDefault(_genReactiveInjector);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* @author Kuitos
* @homepage https://github.com/kuitos/
* @since 2018-06-25 17:01
*/
var SNAPSHOT_PHASE;
(function (SNAPSHOT_PHASE) {
SNAPSHOT_PHASE[SNAPSHOT_PHASE["PATCHING"] = 0] = "PATCHING";
SNAPSHOT_PHASE[SNAPSHOT_PHASE["DONE"] = 1] = "DONE";
})(SNAPSHOT_PHASE || (SNAPSHOT_PHASE = {}));
var phase = SNAPSHOT_PHASE.DONE;
/**
* serialize and deep walk the models of injector to enable the dependencies tracking
* @param model
* @returns {Snapshot} serialization
*/
function walkAndSerialize(model) {
// when model is an array, access the array length to enable tracking
if ((0, _types.isArray)(model)) {
return model.length ? model.map(function (value) {
return walkAndSerialize(value);
}) : [];
}
// when model is a map, access the map size to enable tracking
if ((0, _types.isMap)(model)) {
if (model.size) {
var map_1 = {};
model.forEach(function (value, key) {
map_1[key] = walkAndSerialize(value);
});
return map_1;
}
return {};
}
if ((0, _types.isObject)(model)) {
return Object.keys(model).reduce(function (acc, stateName) {
acc[stateName] = walkAndSerialize(model[stateName]);
return acc;
}, {});
}
return model;
}
/**
* hijack the mobx global state to run a processor after all reactions finished
* @see https://github.com/mobxjs/mobx/blob/master/src/core/reaction.ts#L242
* :dark magic:
* @param {() => void} processor
*/
function processAfterReactionsFinished(processor) {
// compatible with mobx 3
var getGlobalState = _mobx._getGlobalState || /* istanbul ignore next */require('mobx').extras.getGlobalState;
var globalState = getGlobalState();
var previousDescriptor = Object.getOwnPropertyDescriptor(globalState, 'isRunningReactions');
var prevValue = globalState.isRunningReactions;
Object.defineProperty(globalState, 'isRunningReactions', {
get: function () {
return prevValue;
},
set: function (v) {
prevValue = v;
if (v === false) {
Object.defineProperty(globalState, 'isRunningReactions', previousDescriptor);
processor();
}
}
});
}
function applySnapshot(snapshot, injector) {
if (injector === void 0) {
injector = (0, _instantiate.getInjector)();
}
if ((0, _types.isObject)(snapshot)) {
patchSnapshot(snapshot, injector);
}
}
function patchSnapshot(patcher, injector) {
if (injector === void 0) {
injector = (0, _instantiate.getInjector)();
}
var currentModels = injector.dump();
phase = SNAPSHOT_PHASE.PATCHING;
(0, _mobx.runInAction)(function () {
// make a copy of patcher to avoid referencing the original patcher after merge
var clonedPatcher = JSON.parse(JSON.stringify(patcher));
var mergedModels = (0, _mergeWith3.default)(currentModels, clonedPatcher, function (original, source) {
// while source less than original, means the data list has items removed, so the overflowed data should be dropped
if ((0, _types.isArray)(original)) {
original.length = source.length;
}
// while the keys of source object less than original, means some properties should be removed in original after patch
if ((0, _types.isObject)(original)) {
_pull3.default.apply(void 0, [Object.keys(original)].concat(Object.keys(source))).forEach(function (key) {
return delete original[key];
});
}
if ((0, _types.isMap)(original)) {
original.clear();
Object.keys(source).forEach(function (key) {
original.set(key, source[key]);
});
}
});
injector.load(mergedModels);
});
processAfterReactionsFinished(function () {
return phase = SNAPSHOT_PHASE.DONE;
});
}
function getSnapshot(arg1, arg2) {
if (typeof arg1 === 'string') {
var snapshot = walkAndSerialize((arg2 || (0, _instantiate.getInjector)()).dump());
return snapshot[arg1];
} else {
return walkAndSerialize((arg1 || (0, _instantiate.getInjector)()).dump());
}
}
function onSnapshot(arg1, arg2, arg3) {
var snapshot;
var onChange;
var injector;
if (typeof arg1 === 'string') {
onChange = arg2;
injector = (0, _genReactiveInjector2.default)(arg3 || (0, _instantiate.getInjector)());
snapshot = function () {
return getSnapshot(arg1, injector);
};
} else {
onChange = arg1;
injector = (0, _genReactiveInjector2.default)(arg2 || (0, _instantiate.getInjector)());
snapshot = function () {
return getSnapshot(injector);
};
}
(0, _instantiate.setInjector)(injector);
var disposer = (0, _mobx.reaction)(snapshot, function (changedSnapshot) {
// only trigger snapshot listeners when snapshot processed
if (phase === SNAPSHOT_PHASE.DONE) {
onChange(changedSnapshot);
}
}, { equals: _mobx.comparer.structural });
return disposer;
}
;