transmutable
Version:
immutable objects that pretend to be mutable
192 lines (165 loc) • 7.24 kB
JavaScript
;
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
var _require = require('./symbols'),
MUTATION = _require.MUTATION,
WAS_WRITTEN = _require.WAS_WRITTEN,
WAS_ACCESSED = _require.WAS_ACCESSED,
ENTITY = _require.ENTITY,
ENTITIES = _require.ENTITIES;
var _require2 = require('./get-set'),
_get = _require2.get,
_set = _require2.set;
var concatOne = function concatOne(arr, s) {
var newArr = new Array(arr.length + 1);
for (var i = 0; i <= arr.length; i++) {
if (i < arr.length) newArr[i] = arr[i];else newArr[i] = s;
}
return newArr;
};
var concat = concatOne;
function createStage(target, rootPatch) {
var keys = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
return new Proxy(target, {
get: function get(target, name) {
var propPatch = concat(keys, name);
var value = void 0;
var mutation = _get(rootPatch, concat(propPatch, MUTATION));
if (mutation) {
return mutation.value;
} else value = target[name];
if (value && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) == 'object') {
return createStage(value, rootPatch, propPatch);
}
return value;
},
set: function set(target, name, value) {
var oldValue = target[name];
if (value !== oldValue) {
if (Array.isArray(target)) {
var patch = _get(rootPatch, keys);
if (!patch) {
patch = {};
_set(rootPatch, keys, patch);
}
if (!patch[MUTATION]) {
var arrayDraft = target.slice();
// we need to explicitly assign the first changed item
// all next changes will affect arrayDraft directly
// without Proxy (`get` trap will return patch[MUTATION].value)
arrayDraft[name] = value;
patch[MUTATION] = { value: arrayDraft };
}
return true;
}
_set(rootPatch, concat(keys, name), _defineProperty({}, MUTATION, { value: value }));
}
return true;
},
ownKeys: function ownKeys(target) {
var patch = _get(rootPatch, keys.concat([])) || {};
return Array.from(new Set(Reflect.ownKeys(target).concat(Object.keys(patch))));
},
getOwnPropertyDescriptor: function getOwnPropertyDescriptor(target, name) {
var patch = _get(rootPatch, keys) || {};
if (patch[name] && patch[name][MUTATION]) {
return {
configurable: true,
enumerable: true,
value: patch[name][MUTATION].value
};
}
return Reflect.getOwnPropertyDescriptor(target, name);
}
});
}
function applyPatch(node, patch, root, rootPatch) {
if (patch && patch[MUTATION]) {
var mutValue = patch[MUTATION].value;
if (mutValue && mutValue[ENTITY]) {
var id = mutValue[ENTITY];
if (rootPatch[ENTITIES] && rootPatch[ENTITIES] && rootPatch[ENTITIES][id]) {
return rootPatch[ENTITIES][id][MUTATION].value;
}
return root[ENTITIES][mutValue[ENTITY]];
}
return patch[MUTATION].value;
}
var symbols = void 0;
if (node && (typeof node === 'undefined' ? 'undefined' : _typeof(node)) == 'object') {
symbols = Object.getOwnPropertySymbols(node);
}
if (patch && node && (typeof node === 'undefined' ? 'undefined' : _typeof(node)) == 'object'
//&& patch[WAS_ACCESSED]
&& Object.keys(patch).length || symbols && symbols.length) {
var copy = void 0;
if (Array.isArray(node)) copy = node.slice();else {
copy = {};
for (var k in node) {
copy[k] = node[k];
}
if (symbols) {
for (var i = 0; i < symbols.length; i++) {
copy[symbols[i]] = node[symbols[i]];
}
}
}
for (var _k in patch) {
var res = applyPatch(node[_k], patch[_k], root, rootPatch);
copy[_k] = res;
}
var patchSymbols = Object.getOwnPropertySymbols(patch);
for (var _i = 0; _i < patchSymbols.length; _i++) {
var _k2 = patchSymbols[_i];
var _res = applyPatch(node[_k2], patch[_k2], root, rootPatch);
copy[_k2] = _res;
}
return copy;
}
return node;
}
exports.applyPatch = applyPatch;
var diff = require('./diff');
var copyDeep = require('./copyDeep');
var transform = function transform(transformer, original) {
for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
args[_key - 2] = arguments[_key];
}
if (typeof original == 'undefined') {
return transform.bind(null, transformer);
}
if (typeof transformer !== 'function') throw new Error('\n API was changed in 0.5.0 version of Transmutable library.\n Now transform function takes transforming function as a FIRST argument.\n Original state as a SECOND one.\n ');
var patch = void 0;
var result = void 0;
if (typeof Proxy == 'undefined') {
var copy = copyDeep(original);
result = transformer.call.apply(transformer, [copy, copy].concat(args));
patch = diff(original, copy);
} else {
patch = {};
var stage = createStage(original, patch);
result = transformer.call.apply(transformer, [stage, stage].concat(args));
}
if (typeof result != 'undefined') return result;
return applyPatch(original, patch, original, patch);
};
exports.transform = transform;
// we keep Reducer separately because Reducer is meant for use with Redux
// and Transform is for general use.
// now they both share the same API and implementation
// but in future versions it may not be true
exports.Reducer = function () {
throw new Error("Transmutable: to create Redux reducer just use `transform` function with currying (look into docs)");
};
var over = function over(getter, setter, original) {
if (typeof setter == 'undefined') return over.bind(null, getter);
return transform(function (d) {
var relativeStage = _get(d, getter);
var result = setter.call(relativeStage, relativeStage);
if (typeof result != 'undefined') {
_set(d, getter, result);
}
}, original);
};
exports.over = over;
exports.transformAt = over;