reselect.js (9986B)
1 (function (global, factory) { 2 typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : 3 typeof define === 'function' && define.amd ? define(['exports'], factory) : 4 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Reselect = {})); 5 })(this, (function (exports) { 'use strict'; 6 7 // Cache implementation based on Erik Rasmussen's `lru-memoize`: 8 // https://github.com/erikras/lru-memoize 9 var NOT_FOUND = 'NOT_FOUND'; 10 11 function createSingletonCache(equals) { 12 var entry; 13 return { 14 get: function get(key) { 15 if (entry && equals(entry.key, key)) { 16 return entry.value; 17 } 18 19 return NOT_FOUND; 20 }, 21 put: function put(key, value) { 22 entry = { 23 key: key, 24 value: value 25 }; 26 }, 27 getEntries: function getEntries() { 28 return entry ? [entry] : []; 29 }, 30 clear: function clear() { 31 entry = undefined; 32 } 33 }; 34 } 35 36 function createLruCache(maxSize, equals) { 37 var entries = []; 38 39 function get(key) { 40 var cacheIndex = entries.findIndex(function (entry) { 41 return equals(key, entry.key); 42 }); // We found a cached entry 43 44 if (cacheIndex > -1) { 45 var entry = entries[cacheIndex]; // Cached entry not at top of cache, move it to the top 46 47 if (cacheIndex > 0) { 48 entries.splice(cacheIndex, 1); 49 entries.unshift(entry); 50 } 51 52 return entry.value; 53 } // No entry found in cache, return sentinel 54 55 56 return NOT_FOUND; 57 } 58 59 function put(key, value) { 60 if (get(key) === NOT_FOUND) { 61 // TODO Is unshift slow? 62 entries.unshift({ 63 key: key, 64 value: value 65 }); 66 67 if (entries.length > maxSize) { 68 entries.pop(); 69 } 70 } 71 } 72 73 function getEntries() { 74 return entries; 75 } 76 77 function clear() { 78 entries = []; 79 } 80 81 return { 82 get: get, 83 put: put, 84 getEntries: getEntries, 85 clear: clear 86 }; 87 } 88 89 var defaultEqualityCheck = function defaultEqualityCheck(a, b) { 90 return a === b; 91 }; 92 function createCacheKeyComparator(equalityCheck) { 93 return function areArgumentsShallowlyEqual(prev, next) { 94 if (prev === null || next === null || prev.length !== next.length) { 95 return false; 96 } // Do this in a for loop (and not a `forEach` or an `every`) so we can determine equality as fast as possible. 97 98 99 var length = prev.length; 100 101 for (var i = 0; i < length; i++) { 102 if (!equalityCheck(prev[i], next[i])) { 103 return false; 104 } 105 } 106 107 return true; 108 }; 109 } 110 // defaultMemoize now supports a configurable cache size with LRU behavior, 111 // and optional comparison of the result value with existing values 112 function defaultMemoize(func, equalityCheckOrOptions) { 113 var providedOptions = typeof equalityCheckOrOptions === 'object' ? equalityCheckOrOptions : { 114 equalityCheck: equalityCheckOrOptions 115 }; 116 var _providedOptions$equa = providedOptions.equalityCheck, 117 equalityCheck = _providedOptions$equa === void 0 ? defaultEqualityCheck : _providedOptions$equa, 118 _providedOptions$maxS = providedOptions.maxSize, 119 maxSize = _providedOptions$maxS === void 0 ? 1 : _providedOptions$maxS, 120 resultEqualityCheck = providedOptions.resultEqualityCheck; 121 var comparator = createCacheKeyComparator(equalityCheck); 122 var cache = maxSize === 1 ? createSingletonCache(comparator) : createLruCache(maxSize, comparator); // we reference arguments instead of spreading them for performance reasons 123 124 function memoized() { 125 var value = cache.get(arguments); 126 127 if (value === NOT_FOUND) { 128 // @ts-ignore 129 value = func.apply(null, arguments); 130 131 if (resultEqualityCheck) { 132 var entries = cache.getEntries(); 133 var matchingEntry = entries.find(function (entry) { 134 return resultEqualityCheck(entry.value, value); 135 }); 136 137 if (matchingEntry) { 138 value = matchingEntry.value; 139 } 140 } 141 142 cache.put(arguments, value); 143 } 144 145 return value; 146 } 147 148 memoized.clearCache = function () { 149 return cache.clear(); 150 }; 151 152 return memoized; 153 } 154 155 function getDependencies(funcs) { 156 var dependencies = Array.isArray(funcs[0]) ? funcs[0] : funcs; 157 158 if (!dependencies.every(function (dep) { 159 return typeof dep === 'function'; 160 })) { 161 var dependencyTypes = dependencies.map(function (dep) { 162 return typeof dep === 'function' ? "function " + (dep.name || 'unnamed') + "()" : typeof dep; 163 }).join(', '); 164 throw new Error("createSelector expects all input-selectors to be functions, but received the following types: [" + dependencyTypes + "]"); 165 } 166 167 return dependencies; 168 } 169 170 function createSelectorCreator(memoize) { 171 for (var _len = arguments.length, memoizeOptionsFromArgs = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 172 memoizeOptionsFromArgs[_key - 1] = arguments[_key]; 173 } 174 175 var createSelector = function createSelector() { 176 for (var _len2 = arguments.length, funcs = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { 177 funcs[_key2] = arguments[_key2]; 178 } 179 180 var _recomputations = 0; 181 182 var _lastResult; // Due to the intricacies of rest params, we can't do an optional arg after `...funcs`. 183 // So, start by declaring the default value here. 184 // (And yes, the words 'memoize' and 'options' appear too many times in this next sequence.) 185 186 187 var directlyPassedOptions = { 188 memoizeOptions: undefined 189 }; // Normally, the result func or "output selector" is the last arg 190 191 var resultFunc = funcs.pop(); // If the result func is actually an _object_, assume it's our options object 192 193 if (typeof resultFunc === 'object') { 194 directlyPassedOptions = resultFunc; // and pop the real result func off 195 196 resultFunc = funcs.pop(); 197 } 198 199 if (typeof resultFunc !== 'function') { 200 throw new Error("createSelector expects an output function after the inputs, but received: [" + typeof resultFunc + "]"); 201 } // Determine which set of options we're using. Prefer options passed directly, 202 // but fall back to options given to createSelectorCreator. 203 204 205 var _directlyPassedOption = directlyPassedOptions, 206 _directlyPassedOption2 = _directlyPassedOption.memoizeOptions, 207 memoizeOptions = _directlyPassedOption2 === void 0 ? memoizeOptionsFromArgs : _directlyPassedOption2; // Simplifying assumption: it's unlikely that the first options arg of the provided memoizer 208 // is an array. In most libs I've looked at, it's an equality function or options object. 209 // Based on that, if `memoizeOptions` _is_ an array, we assume it's a full 210 // user-provided array of options. Otherwise, it must be just the _first_ arg, and so 211 // we wrap it in an array so we can apply it. 212 213 var finalMemoizeOptions = Array.isArray(memoizeOptions) ? memoizeOptions : [memoizeOptions]; 214 var dependencies = getDependencies(funcs); 215 var memoizedResultFunc = memoize.apply(void 0, [function () { 216 _recomputations++; // apply arguments instead of spreading for performance. 217 218 return resultFunc.apply(null, arguments); 219 }].concat(finalMemoizeOptions)); // If a selector is called with the exact same arguments we don't need to traverse our dependencies again. 220 221 var selector = memoize(function () { 222 var params = []; 223 var length = dependencies.length; 224 225 for (var i = 0; i < length; i++) { 226 // apply arguments instead of spreading and mutate a local list of params for performance. 227 // @ts-ignore 228 params.push(dependencies[i].apply(null, arguments)); 229 } // apply arguments instead of spreading for performance. 230 231 232 _lastResult = memoizedResultFunc.apply(null, params); 233 return _lastResult; 234 }); 235 Object.assign(selector, { 236 resultFunc: resultFunc, 237 memoizedResultFunc: memoizedResultFunc, 238 dependencies: dependencies, 239 lastResult: function lastResult() { 240 return _lastResult; 241 }, 242 recomputations: function recomputations() { 243 return _recomputations; 244 }, 245 resetRecomputations: function resetRecomputations() { 246 return _recomputations = 0; 247 } 248 }); 249 return selector; 250 }; // @ts-ignore 251 252 253 return createSelector; 254 } 255 var createSelector = /* #__PURE__ */createSelectorCreator(defaultMemoize); 256 // Manual definition of state and output arguments 257 var createStructuredSelector = function createStructuredSelector(selectors, selectorCreator) { 258 if (selectorCreator === void 0) { 259 selectorCreator = createSelector; 260 } 261 262 if (typeof selectors !== 'object') { 263 throw new Error('createStructuredSelector expects first argument to be an object ' + ("where each property is a selector, instead received a " + typeof selectors)); 264 } 265 266 var objectKeys = Object.keys(selectors); 267 var resultSelector = selectorCreator( // @ts-ignore 268 objectKeys.map(function (key) { 269 return selectors[key]; 270 }), function () { 271 for (var _len3 = arguments.length, values = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { 272 values[_key3] = arguments[_key3]; 273 } 274 275 return values.reduce(function (composition, value, index) { 276 composition[objectKeys[index]] = value; 277 return composition; 278 }, {}); 279 }); 280 return resultSelector; 281 }; 282 283 exports.createSelector = createSelector; 284 exports.createSelectorCreator = createSelectorCreator; 285 exports.createStructuredSelector = createStructuredSelector; 286 exports.defaultEqualityCheck = defaultEqualityCheck; 287 exports.defaultMemoize = defaultMemoize; 288 289 Object.defineProperty(exports, '__esModule', { value: true }); 290 291 }));