[6a3a178] | 1 | 'use strict';
|
---|
| 2 | const mimicFn = require("mimic-fn");
|
---|
| 3 | const mapAgeCleaner = require("map-age-cleaner");
|
---|
| 4 | const decoratorInstanceMap = new WeakMap();
|
---|
| 5 | const cacheStore = new WeakMap();
|
---|
| 6 | /**
|
---|
| 7 | [Memoize](https://en.wikipedia.org/wiki/Memoization) functions - An optimization used to speed up consecutive function calls by caching the result of calls with identical input.
|
---|
| 8 |
|
---|
| 9 | @param fn - Function to be memoized.
|
---|
| 10 |
|
---|
| 11 | @example
|
---|
| 12 | ```
|
---|
| 13 | import mem = require('mem');
|
---|
| 14 |
|
---|
| 15 | let i = 0;
|
---|
| 16 | const counter = () => ++i;
|
---|
| 17 | const memoized = mem(counter);
|
---|
| 18 |
|
---|
| 19 | memoized('foo');
|
---|
| 20 | //=> 1
|
---|
| 21 |
|
---|
| 22 | // Cached as it's the same arguments
|
---|
| 23 | memoized('foo');
|
---|
| 24 | //=> 1
|
---|
| 25 |
|
---|
| 26 | // Not cached anymore as the arguments changed
|
---|
| 27 | memoized('bar');
|
---|
| 28 | //=> 2
|
---|
| 29 |
|
---|
| 30 | memoized('bar');
|
---|
| 31 | //=> 2
|
---|
| 32 | ```
|
---|
| 33 | */
|
---|
| 34 | const mem = (fn, { cacheKey, cache = new Map(), maxAge } = {}) => {
|
---|
| 35 | if (typeof maxAge === 'number') {
|
---|
| 36 | // TODO: Drop after https://github.com/SamVerschueren/map-age-cleaner/issues/5
|
---|
| 37 | // @ts-expect-error
|
---|
| 38 | mapAgeCleaner(cache);
|
---|
| 39 | }
|
---|
| 40 | const memoized = function (...arguments_) {
|
---|
| 41 | const key = cacheKey ? cacheKey(arguments_) : arguments_[0];
|
---|
| 42 | const cacheItem = cache.get(key);
|
---|
| 43 | if (cacheItem) {
|
---|
| 44 | return cacheItem.data;
|
---|
| 45 | }
|
---|
| 46 | const result = fn.apply(this, arguments_);
|
---|
| 47 | cache.set(key, {
|
---|
| 48 | data: result,
|
---|
| 49 | maxAge: maxAge ? Date.now() + maxAge : Number.POSITIVE_INFINITY
|
---|
| 50 | });
|
---|
| 51 | return result;
|
---|
| 52 | };
|
---|
| 53 | mimicFn(memoized, fn, {
|
---|
| 54 | ignoreNonConfigurable: true
|
---|
| 55 | });
|
---|
| 56 | cacheStore.set(memoized, cache);
|
---|
| 57 | return memoized;
|
---|
| 58 | };
|
---|
| 59 | /**
|
---|
| 60 | @returns A [decorator](https://github.com/tc39/proposal-decorators) to memoize class methods or static class methods.
|
---|
| 61 |
|
---|
| 62 | @example
|
---|
| 63 | ```
|
---|
| 64 | import mem = require('mem');
|
---|
| 65 |
|
---|
| 66 | class Example {
|
---|
| 67 | index = 0
|
---|
| 68 |
|
---|
| 69 | @mem.decorator()
|
---|
| 70 | counter() {
|
---|
| 71 | return ++this.index;
|
---|
| 72 | }
|
---|
| 73 | }
|
---|
| 74 |
|
---|
| 75 | class ExampleWithOptions {
|
---|
| 76 | index = 0
|
---|
| 77 |
|
---|
| 78 | @mem.decorator({maxAge: 1000})
|
---|
| 79 | counter() {
|
---|
| 80 | return ++this.index;
|
---|
| 81 | }
|
---|
| 82 | }
|
---|
| 83 | ```
|
---|
| 84 | */
|
---|
| 85 | mem.decorator = (options = {}) => (target, propertyKey, descriptor) => {
|
---|
| 86 | const input = target[propertyKey];
|
---|
| 87 | if (typeof input !== 'function') {
|
---|
| 88 | throw new TypeError('The decorated value must be a function');
|
---|
| 89 | }
|
---|
| 90 | delete descriptor.value;
|
---|
| 91 | delete descriptor.writable;
|
---|
| 92 | descriptor.get = function () {
|
---|
| 93 | if (!decoratorInstanceMap.has(this)) {
|
---|
| 94 | const value = mem(input, options);
|
---|
| 95 | decoratorInstanceMap.set(this, value);
|
---|
| 96 | return value;
|
---|
| 97 | }
|
---|
| 98 | return decoratorInstanceMap.get(this);
|
---|
| 99 | };
|
---|
| 100 | };
|
---|
| 101 | /**
|
---|
| 102 | Clear all cached data of a memoized function.
|
---|
| 103 |
|
---|
| 104 | @param fn - Memoized function.
|
---|
| 105 | */
|
---|
| 106 | mem.clear = (fn) => {
|
---|
| 107 | const cache = cacheStore.get(fn);
|
---|
| 108 | if (!cache) {
|
---|
| 109 | throw new TypeError('Can\'t clear a function that was not memoized!');
|
---|
| 110 | }
|
---|
| 111 | if (typeof cache.clear !== 'function') {
|
---|
| 112 | throw new TypeError('The cache Map can\'t be cleared!');
|
---|
| 113 | }
|
---|
| 114 | cache.clear();
|
---|
| 115 | };
|
---|
| 116 | module.exports = mem;
|
---|