index.js 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  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 = (...args) => {
  7. if (args.length === 0) {
  8. return '__defaultKey';
  9. }
  10. if (args.length === 1) {
  11. const [firstArgument] = args;
  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(args);
  21. };
  22. module.exports = (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 (...args) {
  40. const key = options.cacheKey(...args);
  41. if (cache.has(key)) {
  42. const c = cache.get(key);
  43. return c.data;
  44. }
  45. const ret = fn.call(this, ...args);
  46. setData(key, ret);
  47. if (isPromise(ret) && options.cachePromiseRejection === false) {
  48. // Remove rejected promises from cache unless `cachePromiseRejection` is set to `true`
  49. ret.catch(() => cache.delete(key));
  50. }
  51. return ret;
  52. };
  53. try {
  54. // The below call will throw in some host environments
  55. // See https://github.com/sindresorhus/mimic-fn/issues/10
  56. mimicFn(memoized, fn);
  57. } catch (_) {}
  58. cacheStore.set(memoized, options.cache);
  59. return memoized;
  60. };
  61. module.exports.clear = fn => {
  62. const cache = cacheStore.get(fn);
  63. if (cache && typeof cache.clear === 'function') {
  64. cache.clear();
  65. }
  66. };