You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

88 lines
1.9 KiB

4 years ago
  1. 'use strict';
  2. const mimicFn = require('mimic-fn');
  3. const isPromise = require('p-is-promise');
  4. const mapAgeCleaner = require('map-age-cleaner');
  5. const cacheStore = new WeakMap();
  6. const defaultCacheKey = (...arguments_) => {
  7. if (arguments_.length === 0) {
  8. return '__defaultKey';
  9. }
  10. if (arguments_.length === 1) {
  11. const [firstArgument] = arguments_;
  12. if (
  13. firstArgument === null ||
  14. firstArgument === undefined ||
  15. (typeof firstArgument !== 'function' && typeof firstArgument !== 'object')
  16. ) {
  17. return firstArgument;
  18. }
  19. }
  20. return JSON.stringify(arguments_);
  21. };
  22. const mem = (fn, options) => {
  23. options = Object.assign({
  24. cacheKey: defaultCacheKey,
  25. cache: new Map(),
  26. cachePromiseRejection: false
  27. }, options);
  28. if (typeof options.maxAge === 'number') {
  29. mapAgeCleaner(options.cache);
  30. }
  31. const {cache} = options;
  32. options.maxAge = options.maxAge || 0;
  33. const setData = (key, data) => {
  34. cache.set(key, {
  35. data,
  36. maxAge: Date.now() + options.maxAge
  37. });
  38. };
  39. const memoized = function (...arguments_) {
  40. const key = options.cacheKey(...arguments_);
  41. if (cache.has(key)) {
  42. return cache.get(key).data;
  43. }
  44. const cacheItem = fn.call(this, ...arguments_);
  45. setData(key, cacheItem);
  46. if (isPromise(cacheItem) && options.cachePromiseRejection === false) {
  47. // Remove rejected promises from cache unless `cachePromiseRejection` is set to `true`
  48. cacheItem.catch(() => cache.delete(key));
  49. }
  50. return cacheItem;
  51. };
  52. try {
  53. // The below call will throw in some host environments
  54. // See https://github.com/sindresorhus/mimic-fn/issues/10
  55. mimicFn(memoized, fn);
  56. } catch (_) {}
  57. cacheStore.set(memoized, options.cache);
  58. return memoized;
  59. };
  60. module.exports = mem;
  61. // TODO: Remove this for the next major release
  62. module.exports.default = mem;
  63. module.exports.clear = fn => {
  64. const cache = cacheStore.get(fn);
  65. if (cache && typeof cache.clear === 'function') {
  66. cache.clear();
  67. }
  68. };