UNPKG

@fantastic-utils/memo

Version:

powerful memorization lib, which inspired by memoize-one, proxy-memoize

427 lines (348 loc) 12.8 kB
import _regeneratorRuntime from "@babel/runtime/helpers/esm/regeneratorRuntime"; import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator"; import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray"; import _createForOfIteratorHelper from "@babel/runtime/helpers/esm/createForOfIteratorHelper"; import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray"; import _defineProperty from "@babel/runtime/helpers/esm/defineProperty"; import { getDataType } from '@fantastic-utils/data-type'; var ORIGINAL_SYMBOL = Symbol('_original'); var AFFECTED_TYPES; (function (AFFECTED_TYPES) { AFFECTED_TYPES["KEYS"] = "k"; AFFECTED_TYPES["HAS_KEY"] = "h"; AFFECTED_TYPES["HAS_OWN_KEY"] = "o"; AFFECTED_TYPES["ALL_KEYS"] = "a"; })(AFFECTED_TYPES || (AFFECTED_TYPES = {})); var defaultShouldCompare = function defaultShouldCompare() { return true; }; // get object prototype var getProto = Object.getPrototypeOf; // Properties that are both non-configurable and non-writable will break // the proxy get trap when we try to return a recursive/child compare proxy // from them. We can avoid this by making a copy of the target object with // all descriptors marked as configurable, see `copyTargetObject`. var needsToCopyTargetObject = function needsToCopyTargetObject(obj) { return Object.values(Object.getOwnPropertyDescriptors(obj)).some(function (descriptor) { return !descriptor.configurable && !descriptor.writable; }); }; // Make a copy with all descriptors marked as configurable. var copyTargetObject = function copyTargetObject(obj) { if (Array.isArray(obj)) { // Arrays need a special way to copy return Array.from(obj); } // For non-array objects, we create a new object keeping the prototype // with changing all configurable options (otherwise, proxies will complain) var descriptors = Object.getOwnPropertyDescriptors(obj); Object.values(descriptors).forEach(function (desc) { desc.configurable = true; }); return Object.create(getProto(obj), descriptors); }; export var original = function original(proxyTarget) { return proxyTarget[ORIGINAL_SYMBOL]; }; export var getUsed = function getUsed(arg, affected) { var used = affected.get(arg); if (!used) { var _used; used = (_used = {}, _defineProperty(_used, AFFECTED_TYPES.KEYS, new Map()), _defineProperty(_used, AFFECTED_TYPES.HAS_KEY, new Map()), _defineProperty(_used, AFFECTED_TYPES.ALL_KEYS, [false, []]), _defineProperty(_used, AFFECTED_TYPES.HAS_OWN_KEY, new Map()), _used); affected.set(arg, used); } return used; }; var createHandler = function createHandler(arg, memoCfg, affected) { var handler = { get: function get(target, key) { if (key === ORIGINAL_SYMBOL) return arg; var keyType = getDataType(key); var reflectValue = Reflect.get(target, key); if (keyType === 'Symbol') return reflectValue; var used = getUsed(arg, affected); var affectedKeysMap = used[AFFECTED_TYPES.KEYS]; var affectedKeyCfg = affectedKeysMap.get(key); if (affectedKeyCfg) { return affectedKeyCfg.v; } var normalizedArgCfg = normalizeArgs(reflectValue, memoCfg, affected); affectedKeysMap.set(key, normalizedArgCfg); return normalizedArgCfg.v; }, has: function has(target, key) { var reflectHas = Reflect.has(target, key); var used = getUsed(arg, affected); var affectedHasKeysMap = used[AFFECTED_TYPES.HAS_KEY]; var affectedHasKeyCfg = affectedHasKeysMap.get(key); if (affectedHasKeyCfg) { return affectedHasKeyCfg.v; } affectedHasKeysMap.set(key, { v: reflectHas }); return reflectHas; }, getOwnPropertyDescriptor: function getOwnPropertyDescriptor(target, key) { var desc = Object.getOwnPropertyDescriptor(target, key); var used = getUsed(arg, affected); var affectedHasOwnKey = used[AFFECTED_TYPES.HAS_OWN_KEY]; var affectedHasOwnKeyCfg = affectedHasOwnKey.get(key); if (!affectedHasOwnKeyCfg) { affectedHasOwnKey.set(key, { v: !!desc }); } return desc; }, ownKeys: function ownKeys(target) { var ownKeys = Reflect.ownKeys(target); var used = getUsed(arg, affected); var _used$AFFECTED_TYPES$ = _slicedToArray(used[AFFECTED_TYPES.ALL_KEYS], 1), allKeysCalled = _used$AFFECTED_TYPES$[0]; if (!allKeysCalled) { used[AFFECTED_TYPES.ALL_KEYS] = [true, ownKeys]; } return ownKeys; } }; return handler; }; var createProxy = function createProxy(arg, memoCfg, affected) { var handler = createHandler(arg, memoCfg, affected); return new Proxy(arg, handler); }; var normalizeArgs = function normalizeArgs(arg, memoCfg, affected) { var dataType = getDataType(arg); switch (dataType) { case 'Object': case 'Array': var used = getUsed(arg, affected); var selfProxy = used === null || used === void 0 ? void 0 : used.p; var needCopy = needsToCopyTargetObject(arg); var proxyValue = selfProxy; var plainArg = arg; if (!proxyValue) { if (needCopy) { plainArg = copyTargetObject(arg); } proxyValue = createProxy(plainArg, memoCfg, affected); used.p = proxyValue; } return { t: dataType, r: needCopy ? plainArg : arg, v: proxyValue, a: affected }; default: return { t: dataType, r: arg, v: arg, a: affected }; } }; var isChanged = function isChanged(normalizedArg, newArg, memoCfg) { var t = normalizedArg.t, r = normalizedArg.r, affected = normalizedArg.a; var objectShallowCompare = memoCfg.objectShallowCompare; if (objectShallowCompare) { return !Object.is(r, newArg); } var newArgType = getDataType(newArg); switch (t) { case 'Array': case 'Object': var used = affected.get(r); if (newArgType !== t || !used) { return true; } var affectedKeys = used[AFFECTED_TYPES.KEYS]; var affectedHasKey = used[AFFECTED_TYPES.HAS_KEY]; var affectedHasOwnKey = used[AFFECTED_TYPES.HAS_OWN_KEY]; var _used$AFFECTED_TYPES$2 = _slicedToArray(used[AFFECTED_TYPES.ALL_KEYS], 2), allKeysCalled = _used$AFFECTED_TYPES$2[0], keys = _used$AFFECTED_TYPES$2[1]; var changed = false; var _iterator = _createForOfIteratorHelper(affectedHasKey || []), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var _step$value = _slicedToArray(_step.value, 2), _key = _step$value[0], hasKeyCfg = _step$value[1]; changed = hasKeyCfg.v !== Reflect.has(newArg, _key); if (changed) break; } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } if (changed) return true; if (allKeysCalled) { var newArgOwnKeys = Reflect.ownKeys(newArg); changed = keys.length !== newArgOwnKeys.length || keys.some(function (k, i) { return k !== newArgOwnKeys[i]; }); if (changed) return changed; } else { var _iterator2 = _createForOfIteratorHelper(affectedHasOwnKey || []), _step2; try { for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { var _step2$value = _slicedToArray(_step2.value, 2), key = _step2$value[0], hasOwnKeyCfg = _step2$value[1]; changed = hasOwnKeyCfg.v !== !!Reflect.getOwnPropertyDescriptor(newArg, key); if (changed) break; } } catch (err) { _iterator2.e(err); } finally { _iterator2.f(); } } if (changed) return true; var _iterator3 = _createForOfIteratorHelper(affectedKeys || []), _step3; try { for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { var _step3$value = _slicedToArray(_step3.value, 2), _key2 = _step3$value[0], affectedKeyArg = _step3$value[1]; changed = isChanged(affectedKeyArg, newArg[_key2], memoCfg); if (changed) break; } } catch (err) { _iterator3.e(err); } finally { _iterator3.f(); } if (changed) return true; return false; default: return !Object.is(r, newArg); } }; var isArgsChanged = function isArgsChanged(cachedProxyArgCfg, newArgs, memoCfg) { var shouldCompare = memoCfg.shouldCompare; if (shouldCompare && !shouldCompare(newArgs, cachedProxyArgCfg)) { return false; } var argLength = cachedProxyArgCfg.length; if (argLength !== newArgs.length) { return true; } var changed = false; for (var i = 0; i < argLength; i++) { changed = isChanged(cachedProxyArgCfg[i], newArgs[i], memoCfg); if (changed) break; } return changed; }; var getCachedProxyArgCfg = function getCachedProxyArgCfg(args, memoCfg) { var proxyArgs = []; var cachedProxyArgsCfg = args.reduce(function (prev, arg) { var affected = new WeakMap(); var normalizedArg = normalizeArgs(arg, memoCfg, affected); prev.push(normalizedArg); proxyArgs.push(normalizedArg.v); return prev; }, []); return [proxyArgs, cachedProxyArgsCfg]; }; var untrack = function untrack(x, seen) { if (['Object', 'Array'].indexOf(getDataType(x)) === -1) return x; var untrackedObj = original(x); if (untrackedObj) { return untrackedObj; } if (!seen.has(x)) { seen.add(x); Object.entries(x).forEach(function (_ref) { var _ref2 = _slicedToArray(_ref, 2), k = _ref2[0], v = _ref2[1]; var vv = untrack(v, seen); if (!Object.is(vv, v)) x[k] = vv; }); } return x; }; export var memo = function memo(fn) { var memoCfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { objectShallowCompare: false, shouldCompare: defaultShouldCompare }; var customIsChanged = memoCfg.isChanged; var cachedProxyArgsCfg = []; var cachedResult; return function () { var proxyArgs = []; for (var _len = arguments.length, args = new Array(_len), _key3 = 0; _key3 < _len; _key3++) { args[_key3] = arguments[_key3]; } var changed = customIsChanged ? customIsChanged(args, cachedProxyArgsCfg) : isArgsChanged(cachedProxyArgsCfg, args, memoCfg); if (!changed) { return cachedResult; } var _getCachedProxyArgCfg = getCachedProxyArgCfg(args, memoCfg); var _getCachedProxyArgCfg2 = _slicedToArray(_getCachedProxyArgCfg, 2); proxyArgs = _getCachedProxyArgCfg2[0]; cachedProxyArgsCfg = _getCachedProxyArgCfg2[1]; var rt = fn.apply(void 0, _toConsumableArray(proxyArgs)); cachedResult = untrack(rt, new Set()); return cachedResult; }; }; export var memoAsync = function memoAsync(fn) { var memoCfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { objectShallowCompare: false, shouldCompare: defaultShouldCompare }; var customIsChanged = memoCfg.isChanged; var cachedProxyArgsCfg = []; var cachedResult; return /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee() { var proxyArgs, _len2, args, _key4, changed, _getCachedProxyArgCfg3, _getCachedProxyArgCfg4, rt, _args = arguments; return _regeneratorRuntime().wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: proxyArgs = []; for (_len2 = _args.length, args = new Array(_len2), _key4 = 0; _key4 < _len2; _key4++) { args[_key4] = _args[_key4]; } changed = customIsChanged ? customIsChanged(args, cachedProxyArgsCfg) : isArgsChanged(cachedProxyArgsCfg, args, memoCfg); if (changed) { _context.next = 5; break; } return _context.abrupt("return", cachedResult); case 5: _getCachedProxyArgCfg3 = getCachedProxyArgCfg(args, memoCfg); _getCachedProxyArgCfg4 = _slicedToArray(_getCachedProxyArgCfg3, 2); proxyArgs = _getCachedProxyArgCfg4[0]; cachedProxyArgsCfg = _getCachedProxyArgCfg4[1]; _context.next = 11; return fn.apply(void 0, _toConsumableArray(proxyArgs)); case 11: rt = _context.sent; cachedResult = untrack(rt, new Set()); return _context.abrupt("return", cachedResult); case 14: case "end": return _context.stop(); } } }, _callee); })); };