UNPKG

10 kBJavaScriptView Raw
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 recomputationWrapper() {
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 dependenciesChecker() {
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}));