lion/packages-node/providence-analytics/src/program/utils/memoize.js

75 lines
1.8 KiB
JavaScript

/**
* For testing purposes, it is possible to disable caching.
*/
let shouldCache = true;
/**
* @param {object|any[]|string} arg
*/
function isObject(arg) {
return !Array.isArray(arg) && typeof arg === 'object';
}
/**
* @param {object|any[]|string} arg
*/
function createCachableArg(arg) {
if (!isObject(arg)) return arg;
try {
return JSON.stringify(arg);
} catch {
return arg;
}
}
/**
* @template T
* @type {<T extends Function>(functionToMemoize:T, opts?:{ cacheStorage?:object; }) => T & {clearCache:() => void}}
*/
export function memoize(functionToMemoize, { cacheStorage = {} } = {}) {
function memoizedFn() {
// eslint-disable-next-line prefer-rest-params
const args = [...arguments];
const shouldSerialize = args.some(isObject);
const cachableArgs = shouldSerialize ? args.map(createCachableArg) : args;
// Allow disabling of cache for testing purposes
// @ts-expect-error
if (shouldCache && cachableArgs in cacheStorage) {
// @ts-expect-error
return cacheStorage[cachableArgs];
}
// @ts-expect-error
const outcome = functionToMemoize.apply(this, args);
// @ts-expect-error
// eslint-disable-next-line no-param-reassign
cacheStorage[cachableArgs] = outcome;
return outcome;
}
memoizedFn.clearCache = () => {
// eslint-disable-next-line no-param-reassign
cacheStorage = {};
};
return /** @type {* & T & {clearCache:() => void}} */ (memoizedFn);
}
/**
* For testing purposes, it is possible to disable caching.
*/
memoize.disableCaching = () => {
shouldCache = false;
};
/**
* Once testing is done, it is possible to restore caching.
* @param {boolean} [initialValue]
*/
memoize.restoreCaching = initialValue => {
shouldCache = initialValue || true;
};
Object.defineProperty(memoize, 'isCacheEnabled', {
get() {
return shouldCache;
},
});