Full Code of erikras/lru-memoize for AI

master fab5d613facd cached
19 files
24.0 KB
7.5k tokens
6 symbols
1 requests
Download .txt
Repository: erikras/lru-memoize
Branch: master
Commit: fab5d613facd
Files: 19
Total size: 24.0 KB

Directory structure:
gitextract_dta5t2p_/

├── .babelrc
├── .flowconfig
├── .gitignore
├── .npmignore
├── .travis.yml
├── LICENSE
├── README.md
├── package-scripts.js
├── package.json
├── rollup.config.js
└── src/
    ├── __tests__/
    │   ├── deepEquals.spec.js
    │   └── memoize.spec.js
    ├── deepEquals.js
    ├── index.d.ts
    ├── index.js
    ├── index.js.flow
    ├── lruCache.js
    ├── memoize.js
    └── singletonCache.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .babelrc
================================================
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "loose": true,
        "targets": {
          "node": "8"
        }
      }
    ],
    "@babel/preset-flow"
  ],
  "plugins": [
    "@babel/plugin-transform-flow-strip-types",
    "@babel/plugin-syntax-dynamic-import",
    "@babel/plugin-syntax-import-meta",
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-proposal-json-strings",
    [
      "@babel/plugin-proposal-decorators",
      {
        "legacy": true
      }
    ],
    "@babel/plugin-proposal-function-sent",
    "@babel/plugin-proposal-export-namespace-from",
    "@babel/plugin-proposal-numeric-separator",
    "@babel/plugin-proposal-throw-expressions"
  ]
}


================================================
FILE: .flowconfig
================================================
[ignore]
dist/.*

[include]

[libs]

[options]
esproposal.decorators=ignore


================================================
FILE: .gitignore
================================================
.idea
node_modules
coverage
dist
lib
npm-debug.log
.DS_Store


================================================
FILE: .npmignore
================================================
src
test
scripts


================================================
FILE: .travis.yml
================================================
sudo: false
language: node_js
before_install:
  - npm install -g npm@6.7.0
cache:
  directories:
    - node_modules
notifications:
  email: false
node_js:
  - '8'
  - '9'
  - '10'
  - '11'
  - 'stable'
script:
  - npm start validate
after_success:
  - npx codecov
branches:
  only:
    - master

================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2017 Erik Rasmussen

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# lru-memoize

[![NPM Version](https://img.shields.io/npm/v/lru-memoize.svg?style=flat-square)](https://www.npmjs.com/package/lru-memoize) 
[![NPM Downloads](https://img.shields.io/npm/dm/lru-memoize.svg?style=flat-square)](https://www.npmjs.com/package/lru-memoize)
[![Build Status](https://img.shields.io/travis/erikras/lru-memoize/master.svg?style=flat-square)](https://travis-ci.org/erikras/lru-memoize)

`lru-memoize` is a utility to provide simple memoization for any pure javascript function, using an [LRU cache](https://en.wikipedia.org/wiki/Cache_algorithms) that prioritizes the most recently accessed values, and discards the "least recently used" (LRU) items when the size limit is reached. _If your function has side effects or relies on some external state to generate its result, it should not be memoized._

## Installation

```
npm install --save lru-memoize
```

## Usage

Let's look at an example where we want to memoize a function that multiplies three numbers together, and we want to keep the last ten `arguments -> value` mappings in memory.

### ES5

```javascript
var memoize = require('lru-memoize');

var multiply = function(a, b, c) {
  return a * b * c;
}

multiply = memoize(10)(multiply);

module.exports = multiply;
```

### ES6

```javascript
import memoize from 'lru-memoize';

let multiply = (a, b, c) => a * b * c;

multiply = memoize(10)(multiply);

export default multiply;
```

## API

#### `memoize(limit:Integer?, equals:Function?, deepObjects:Boolean?)`

Returns `(Function) => Function`.

###### -`limit` : Integer [optional]

> The number of `arguments -> value` mappings to keep in memory. Defaults to `1`.

###### -`equals` : Function [optional]

> A function to compare two values for equality. Defaults to `===`.

###### -`deepObjects` : Boolean [optional]

> Whether or not to perform a deep equals on Object values. Defaults to `false`.



================================================
FILE: package-scripts.js
================================================
const npsUtils = require("nps-utils");

const series = npsUtils.series;
const concurrent = npsUtils.concurrent;
const rimraf = npsUtils.rimraf;
const crossEnv = npsUtils.crossEnv;

module.exports = {
  scripts: {
    test: {
      default: crossEnv("NODE_ENV=test jest --coverage"),
      update: crossEnv("NODE_ENV=test jest --coverage --updateSnapshot"),
      watch: crossEnv("NODE_ENV=test jest --watch"),
      codeCov: crossEnv(
        "cat ./coverage/lcov.info | ./node_modules/codecov.io/bin/codecov.io.js"
      ),
      size: {
        description: "check the size of the bundle",
        script: "bundlesize"
      }
    },
    build: {
      description: "delete the dist directory and run all builds",
      default: series(
        rimraf("dist"),
        concurrent.nps(
          "build.es",
          "build.cjs",
          "build.umd.main",
          "build.umd.min",
          "copyTypes"
        )
      ),
      es: {
        description: "run the build with rollup (uses rollup.config.js)",
        script: "rollup --config --environment FORMAT:es"
      },
      cjs: {
        description: "run rollup build with CommonJS format",
        script: "rollup --config --environment FORMAT:cjs"
      },
      umd: {
        min: {
          description: "run the rollup build with sourcemaps",
          script: "rollup --config --sourcemap --environment MINIFY,FORMAT:umd"
        },
        main: {
          description: "builds the cjs and umd files",
          script: "rollup --config --sourcemap --environment FORMAT:umd"
        }
      },
      andTest: series.nps("build", "test.size")
    },
    docs: {
      description: "Generates table of contents in README",
      script: "doctoc README.md"
    },
    copyTypes: series(
      npsUtils.copy("src/*.js.flow src/*.d.ts dist"),
      npsUtils.copy(
        'dist/index.js.flow dist --rename="lru-memoize.cjs.js.flow"'
      ),
      npsUtils.copy('dist/index.js.flow dist --rename="lru-memoize.es.js.flow"')
    ),
    flow: {
      description: "flow check the entire project",
      script: "flow check"
    },
    validate: {
      description:
        "This runs several scripts to make sure things look good before committing or on clean install",
      default: concurrent.nps("flow", "build.andTest", "test")
    }
  },
  options: {
    silent: false
  }
};


================================================
FILE: package.json
================================================
{
  "name": "lru-memoize",
  "version": "1.1.0",
  "description": "A utility to provide lru memoization for any js function",
  "main": "dist/lru-memoize.cjs.js",
  "jsnext:main": "dist/lru-memoize.es.js",
  "module": "dist/lru-memoize.es.js",
  "typings": "dist/index.d.ts",
  "repository": {
    "type": "git",
    "url": "https://github.com/erikras/lru-memoize"
  },
  "scripts": {
    "start": "nps",
    "test": "nps test",
    "prepare": "lint-staged && npm start validate"
  },
  "keywords": [
    "memoize",
    "cache",
    "caching",
    "es7",
    "decorator"
  ],
  "author": "Erik Rasmussen <rasmussenerik@gmail.com> (http://github.com/erikras)",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/erikras/lru-memoize/issues"
  },
  "homepage": "https://github.com/erikras/lru-memoize",
  "devDependencies": {
    "@babel/cli": "^7.0.0",
    "@babel/core": "^7.0.0",
    "@babel/plugin-proposal-class-properties": "^7.3.3",
    "@babel/plugin-proposal-decorators": "^7.3.0",
    "@babel/plugin-proposal-export-namespace-from": "^7.2.0",
    "@babel/plugin-proposal-function-sent": "^7.2.0",
    "@babel/plugin-proposal-json-strings": "^7.2.0",
    "@babel/plugin-proposal-numeric-separator": "^7.2.0",
    "@babel/plugin-proposal-throw-expressions": "^7.2.0",
    "@babel/plugin-syntax-dynamic-import": "^7.2.0",
    "@babel/plugin-syntax-import-meta": "^7.2.0",
    "@babel/plugin-transform-flow-strip-types": "^7.2.3",
    "@babel/plugin-transform-runtime": "^7.2.0",
    "@babel/preset-env": "^7.0.0",
    "@babel/preset-flow": "^7.0.0",
    "@babel/register": "^7.0.0",
    "babel-jest": "^24.1.0",
    "babel-loader": "^8.0.5",
    "bundlesize": "^0.17.1",
    "flow-bin": "^0.93.0",
    "glow": "^1.2.2",
    "husky": "^1.3.1",
    "jest": "^24.1.0",
    "lint-staged": "^8.1.4",
    "nps": "^5.9.3",
    "nps-utils": "^1.7.0",
    "nyc": "^13.2.0",
    "prettier": "^1.16.4",
    "rifraf": "^2.0.3",
    "rimraf": "^2.6.1",
    "rollup": "^1.2.2",
    "rollup-plugin-babel": "^4.3.2",
    "rollup-plugin-commonjs": "^9.2.0",
    "rollup-plugin-flow": "^1.1.1",
    "rollup-plugin-node-resolve": "^4.0.0",
    "rollup-plugin-replace": "^2.1.0",
    "rollup-plugin-uglify": "^6.0.2"
  },
  "lint-staged": {
    "*.{js*,ts*,json,md,css}": [
      "prettier --write",
      "git add"
    ]
  },
  "jest": {
    "testEnvironment": "node",
    "testPathIgnorePatterns": [
      ".*\\.ts"
    ]
  },
  "bundlesize": [
    {
      "path": "dist/lru-memoize.umd.min.js",
      "maxSize": "1kB"
    },
    {
      "path": "dist/lru-memoize.es.js",
      "maxSize": "2kB"
    },
    {
      "path": "dist/lru-memoize.cjs.js",
      "maxSize": "2kB"
    }
  ],
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged && npm start validate"
    }
  }
}


================================================
FILE: rollup.config.js
================================================
import resolve from "rollup-plugin-node-resolve";
import babel from "rollup-plugin-babel";
import flow from "rollup-plugin-flow";
import commonjs from "rollup-plugin-commonjs";
import { uglify } from "rollup-plugin-uglify";
import replace from "rollup-plugin-replace";
import pkg from "./package.json";

const makeExternalPredicate = externalArr => {
  if (externalArr.length === 0) {
    return () => false;
  }
  const pattern = new RegExp(`^(${externalArr.join("|")})($|/)`);
  return id => pattern.test(id);
};

const minify = process.env.MINIFY;
const format = process.env.FORMAT;
const es = format === "es";
const umd = format === "umd";
const cjs = format === "cjs";

let output;

if (es) {
  output = { file: `dist/lru-memoize.es.js`, format: "es" };
} else if (umd) {
  if (minify) {
    output = {
      file: `dist/lru-memoize.umd.min.js`,
      format: "umd"
    };
  } else {
    output = { file: `dist/lru-memoize.umd.js`, format: "umd" };
  }
} else if (cjs) {
  output = { file: `dist/lru-memoize.cjs.js`, format: "cjs" };
} else if (format) {
  throw new Error(`invalid format specified: "${format}".`);
} else {
  throw new Error("no format specified. --environment FORMAT:xxx");
}

export default {
  input: "src/index.js",
  output: Object.assign(
    {
      name: "lru-memoize",
      exports: "named"
    },
    output
  ),
  external: makeExternalPredicate(
    umd
      ? Object.keys(pkg.peerDependencies || {})
      : [
          ...Object.keys(pkg.dependencies || {}),
          ...Object.keys(pkg.peerDependencies || {})
        ]
  ),
  plugins: [
    resolve({ jsnext: true, main: true }),
    flow(),
    commonjs({ include: "node_modules/**" }),
    babel({
      exclude: "node_modules/**",
      babelrc: false,
      runtimeHelpers: true,
      presets: [
        [
          "@babel/preset-env",
          {
            modules: false,
            loose: true
          }
        ],
        "@babel/preset-flow"
      ],
      plugins: [
        ["@babel/plugin-transform-runtime", { useESModules: !cjs }],
        "@babel/plugin-transform-flow-strip-types",
        "@babel/plugin-syntax-dynamic-import",
        "@babel/plugin-syntax-import-meta",
        "@babel/plugin-proposal-class-properties",
        "@babel/plugin-proposal-json-strings",
        [
          "@babel/plugin-proposal-decorators",
          {
            legacy: true
          }
        ],
        "@babel/plugin-proposal-function-sent",
        "@babel/plugin-proposal-export-namespace-from",
        "@babel/plugin-proposal-numeric-separator",
        "@babel/plugin-proposal-throw-expressions"
      ]
    }),
    umd || es
      ? replace({
          "process.env.NODE_ENV": JSON.stringify(
            minify ? "production" : "development"
          )
        })
      : null,
    minify ? uglify() : null
  ].filter(Boolean)
};


================================================
FILE: src/__tests__/deepEquals.spec.js
================================================
import expect from 'expect'
import deepEquals from '../deepEquals'

const tripleEquals = deepEquals((valueA, valueB) => valueA === valueB, true)

describe('deepEquals', () => {
  it('should return true if argument fields are equal', () => {
    expect(tripleEquals(3, 3)).toBe(true)

    expect(tripleEquals('dog', 'dog')).toBe(true)

    expect(
      tripleEquals({ a: 1, b: 2, c: undefined }, { a: 1, b: 2, c: undefined })
    ).toBe(true)

    expect(tripleEquals({ a: 1, b: 2, c: 3 }, { a: 1, b: 2, c: 3 })).toBe(true)

    const obj = {}
    expect(tripleEquals({ a: 1, b: 2, c: obj }, { a: 1, b: 2, c: obj })).toBe(
      true
    )

    expect(tripleEquals(null, null)).toBe(true)
  })

  it('should return false if arguments are number and string', () => {
    expect(tripleEquals(2, '2')).toBe(false)
  })

  it('should return false if arguments are string and number', () => {
    expect(tripleEquals('2', 2)).toBe(false)
  })

  it('should return false if arguments are number and object', () => {
    expect(tripleEquals(4, {})).toBe(false)
  })

  it('should return false if arguments are object and number', () => {
    expect(tripleEquals({}, 4)).toBe(false)
  })

  it('should return false if arguments are number and array', () => {
    expect(tripleEquals(4, [])).toBe(false)
  })

  it('should return false if arguments are array and number', () => {
    expect(tripleEquals([], 4)).toBe(false)
  })

  it('should return false if arguments are string and object', () => {
    expect(tripleEquals('cat', {})).toBe(false)
  })

  it('should return false if arguments are object and string', () => {
    expect(tripleEquals({}, 'cat')).toBe(false)
  })

  it('should return false if arguments are string and array', () => {
    expect(tripleEquals('cat', ['c', 'a', 't'])).toBe(false)
  })

  it('should return false if arguments are array and string', () => {
    expect(tripleEquals(['c', 'a', 't'], 'cat')).toBe(false)
  })

  it('should return false if arguments are array and object', () => {
    expect(tripleEquals([], {})).toBe(false)
  })

  it('should return false if arguments are object and array', () => {
    expect(tripleEquals({}, [])).toBe(false)
  })

  it('should return false if arguments are object and null', () => {
    expect(tripleEquals({ a: 1 }, null)).toBe(false)
  })

  it('should return false if arguments are null and object', () => {
    expect(tripleEquals(null, { a: 1 })).toBe(false)
  })

  it('should return false if first argument has too many keys', () => {
    expect(tripleEquals({ a: 1, b: 2, c: 3 }, { a: 1, b: 2 })).toBe(false)
  })

  it('should return false if second argument has too many keys', () => {
    expect(tripleEquals({ a: 1, b: 2 }, { a: 1, b: 2, c: 3 })).toBe(false)
  })

  it('should return false if arguments have different keys', () => {
    expect(
      tripleEquals({ a: 1, b: 2, c: undefined }, { a: 1, bb: 2, c: undefined })
    ).toBe(false)
  })

  it('should return false if first array argument has too many items', () => {
    expect(tripleEquals([1, 2, 3, 4], [1, 2, 3])).toBe(false)
  })

  it('should return false if second array argument has too many items', () => {
    expect(tripleEquals([1, 2, 3], [1, 2, 3, 4])).toBe(false)
  })

  it('should work with objects inside arrays', () => {
    expect(tripleEquals(
      [ { val: 4 }, { val: 2 }, { val: 3 } ],
      [ { val: 1 }, { val: 2 }, { val: 3 } ]
    )).toBe(false)
  })
})


================================================
FILE: src/__tests__/memoize.spec.js
================================================
import expect from 'expect'
import memoize from '../memoize'

describe('memoize', () => {
  describe('basic', () => {
    it('single', () => {
      let callCount = 0
      let multiply = (x, y, z) => {
        callCount += 1
        return x * y * z
      }
      multiply = memoize()(multiply)

      expect(multiply(1, 2, 3)).toBe(6)

      expect(multiply(1, 2, 3)).toBe(6)

      expect(callCount).toBe(1)

      expect(multiply(4, 5, 6)).toBe(120)

      expect(callCount).toBe(2)

      expect(multiply(1, 2, 3)).toBe(6)

      expect(callCount).toBe(3)
    })

    it('multiple', () => {
      let callCount = 0
      let multiply = (x, y, z) => {
        callCount += 1
        return x * y * z
      }
      multiply = memoize(2)(multiply)

      expect(multiply(1, 2, 3)).toBe(6)

      expect(multiply(1, 2, 3)).toBe(6)

      expect(callCount).toBe(1)

      expect(multiply(4, 5, 6)).toBe(120)

      expect(callCount).toBe(2)

      expect(multiply(1, 2, 3)).toBe(6)

      expect(callCount).toBe(2)

      expect(multiply(4, 5, 6)).toBe(120)

      expect(callCount).toBe(2)
    })
  })

  describe('shallow', () => {
    it('array', () => {
      let callCount = 0
      let multiply = (x, y, z) => {
        callCount += 1
        return x.concat(y, z).reduce((t, n) => t * n)
      }
      multiply = memoize()(multiply)

      const x = [1, 2, 3]
      const y = [4, 5, 6]
      const z = [7, 8, 9]

      const x2 = [1, 2, 3]

      expect(multiply(x, y, z)).toBe(362880)

      expect(multiply(x2, y, z)).toBe(362880)

      expect(callCount).toBe(2)
    })

    it('object', () => {
      let callCount = 0
      let multiply = (x, y, z) => {
        callCount += 1
        return x.val * y.val * z.val
      }
      multiply = memoize(2)(multiply)

      const x = { val: 1 }
      const y = { val: 2 }
      const z = { val: 3 }

      const x2 = { val: 1 }

      expect(multiply(x, y, z)).toBe(6)

      expect(multiply(x2, y, z)).toBe(6)

      expect(callCount).toBe(2)
    })
  })

  describe('deep', () => {
    it('array', () => {
      let callCount = 0
      let multiply = (x, y, z) => {
        callCount += 1
        return x.concat(y, z).reduce((t, n) => t * n)
      }
      multiply = memoize(true)(multiply)

      const x = [1, 2, 3]
      const y = [4, 5, 6]
      const z = [7, 8, 9]

      const x2 = [1, 2, 3]
      const x3 = [3, 2, 1]

      expect(multiply(x, y, z)).toBe(362880)

      expect(multiply(x2, y, z)).toBe(362880)

      expect(callCount).toBe(1)

      expect(multiply(x3, y, z)).toBe(362880)

      expect(callCount).toBe(2)
    })

    it('object', () => {
      let callCount = 0
      let multiply = (x, y, z) => {
        callCount += 1
        return x.val * y.val * z.val
      }
      multiply = memoize(true)(multiply)

      const x = { val: 1 }
      const y = { val: 2 }
      const z = { val: 3 }

      const x2 = { val: 1 }
      const x3 = { val: 4 }

      expect(multiply(x, y, z)).toBe(6)

      expect(multiply(x2, y, z)).toBe(6)

      expect(callCount).toBe(1)

      expect(multiply(x3, y, z)).toBe(24)

      expect(callCount).toBe(2)
    })

    it('object nested', () => {
      let callCount = 0
      let multiply = (x, y, z) => {
        callCount += 1
        return x.inner.val * y.inner.val * z.inner.val
      }
      multiply = memoize(true)(multiply)

      const x = { inner: { val: 1 } }
      const y = { inner: { val: 2 } }
      const z = { inner: { val: 3 } }

      const x2 = { inner: { val: 1 } }
      const x3 = { inner: { val: 4 } }

      expect(multiply(x, y, z)).toBe(6)

      expect(multiply(x2, y, z)).toBe(6)

      expect(callCount).toBe(1)

      expect(multiply(x3, y, z)).toBe(24)

      expect(callCount).toBe(2)
    })
  })
})


================================================
FILE: src/deepEquals.js
================================================
// @flow
import type { Equals } from ".";
const hasOwn = (object, key) =>
  Object.prototype.hasOwnProperty.call(object, key);
export default function deepEquals(equals: Equals, deepObjects: boolean) {
  function deep(valueA: any, valueB: any) {
    if (equals(valueA, valueB)) {
      return true;
    }

    if (Array.isArray(valueA)) {
      if (!Array.isArray(valueB) || valueA.length !== valueB.length) {
        return false;
      }

      // Check deep equality of each value in A against the same indexed value in B
      if (!valueA.every((value, index) => deep(value, valueB[index]))) {
        return false;
      }

      // could not find unequal items
      return true;
    }

    if (Array.isArray(valueB)) {
      return false;
    }

    if (typeof valueA === "object") {
      if (typeof valueB !== "object") {
        return false;
      }

      const isANull = valueA === null;
      const isBNull = valueB === null;
      if (isANull || isBNull) {
        return isANull === isBNull;
      }

      const aKeys = Object.keys(valueA);
      const bKeys = Object.keys(valueB);

      if (aKeys.length !== bKeys.length) {
        return false;
      }

      // Should we compare with shallow equivalence or deep equivalence?
      const equalityChecker = deepObjects ? deep : equals;

      // Check if objects share same keys, and each of those keys are equal
      if (
        !aKeys.every(
          aKey =>
            hasOwn(valueA, aKey) &&
            hasOwn(valueB, aKey) &&
            equalityChecker(valueA[aKey], valueB[aKey])
        )
      ) {
        return false;
      }

      // could not find unequal keys or values
      return true;
    }
    return false;
  }

  return deep;
}


================================================
FILE: src/index.d.ts
================================================
declare const memoize = (
  limit?: number,
  equals?: (a: any, b: any) => boolean,
  deepObjects?: boolean
) => <T extends Function>(func: T) => T;
export default memoize;


================================================
FILE: src/index.js
================================================
import memoize from './memoize'

export default memoize


================================================
FILE: src/index.js.flow
================================================
// @flow
export type Entry = {
  key: any,
  value: any
};
type ConfigItem = number | Function | boolean | void;
export type Config = ConfigItem[];
export type Equals = (any, any) => boolean;

declare export default function memoize(Config): Function => Function;


================================================
FILE: src/lruCache.js
================================================
// @flow
import type { Entry, Equals } from ".";

const findIndex = (arr: any[], fn: any => any) => {
  for (let i = 0; i < arr.length; i++) {
    if (fn(arr[i])) {
      return i;
    }
  }

  return -1;
};

export default function lruCache(limit: number, equals: Equals) {
  const entries: Entry[] = [];

  function get(key: any) {
    const cacheIndex = findIndex(entries, entry => equals(key, entry.key));

    // We found a cached entry
    if (cacheIndex > -1) {
      const entry = entries[cacheIndex];

      // Cached entry not at top of cache, move it to the top
      if (cacheIndex > 0) {
        entries.splice(cacheIndex, 1);
        entries.unshift(entry);
      }

      return entry.value;
    }

    // No entry found in cache, return null
    return undefined;
  }

  function put(key: any, value: any) {
    if (!get(key)) {
      entries.unshift({ key, value });
      if (entries.length > limit) {
        entries.pop();
      }
    }
  }

  return { get, put };
}


================================================
FILE: src/memoize.js
================================================
// @flow
import type { Config, Equals } from ".";
import deepEquals from "./deepEquals";
import lruCache from "./lruCache";
import singletonCache from "./singletonCache";

function createCache(limit: number, equals: Equals) {
  return limit === 1 ? singletonCache(equals) : lruCache(limit, equals);
}

function createEqualsFn(basicEquals: Equals, deepObjects: boolean) {
  // Choose strategy for basic or deep object equals
  const equals = deepObjects
    ? deepEquals(basicEquals, deepObjects)
    : basicEquals;

  return (valueA, valueB) => {
    // The arguments are always the argument array-like objects

    // Different lengths means they are not the same
    if (valueA.length !== valueB.length) {
      return false;
    }

    // Compare the values
    for (let index = 0; index < valueA.length; index += 1) {
      if (!equals(valueA[index], valueB[index])) {
        return false;
      }
    }
    // Found no conflicts
    return true;
  };
}

export default function memoize(...config: Config): Function => Function {
  let limit: number = 1;
  let equals: Equals = (valueA, valueB) => valueA === valueB;
  let deepObjects: boolean = false;

  if (typeof config[0] === "number") {
    limit = ((config.shift(): any): number);
  }
  if (typeof config[0] === "function") {
    equals = ((config.shift(): any): Function);
  } else if (typeof config[0] === "undefined") {
    // Support passing undefined equal argument;
    config.shift();
  }
  if (typeof config[0] === "boolean") {
    deepObjects = config[0];
  }

  const cache = createCache(limit, createEqualsFn(equals, deepObjects));

  return fn => (...args: any[]) => {
    let value = cache.get(args);
    if (value === undefined) {
      value = fn.apply(fn, args);
      cache.put(args, value);
    }
    return value;
  };
}


================================================
FILE: src/singletonCache.js
================================================
// @flow
import type { Entry, Equals } from ".";

export default function singletonCache(equals: Equals) {
  let entry: Entry;
  return {
    get(key: any) {
      if (entry && equals(key, entry.key)) {
        return entry.value;
      }
    },

    put(key: any, value: any) {
      entry = { key, value };
    }
  };
}
Download .txt
gitextract_dta5t2p_/

├── .babelrc
├── .flowconfig
├── .gitignore
├── .npmignore
├── .travis.yml
├── LICENSE
├── README.md
├── package-scripts.js
├── package.json
├── rollup.config.js
└── src/
    ├── __tests__/
    │   ├── deepEquals.spec.js
    │   └── memoize.spec.js
    ├── deepEquals.js
    ├── index.d.ts
    ├── index.js
    ├── index.js.flow
    ├── lruCache.js
    ├── memoize.js
    └── singletonCache.js
Download .txt
SYMBOL INDEX (6 symbols across 4 files)

FILE: src/deepEquals.js
  function deepEquals (line 5) | function deepEquals(equals: Equals, deepObjects: boolean) {

FILE: src/lruCache.js
  function lruCache (line 14) | function lruCache(limit: number, equals: Equals) {

FILE: src/memoize.js
  function createCache (line 7) | function createCache(limit: number, equals: Equals) {
  function createEqualsFn (line 11) | function createEqualsFn(basicEquals: Equals, deepObjects: boolean) {
  function memoize (line 36) | function memoize(...config: Config): Function => Function {

FILE: src/singletonCache.js
  function singletonCache (line 4) | function singletonCache(equals: Equals) {
Condensed preview — 19 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (27K chars).
[
  {
    "path": ".babelrc",
    "chars": 707,
    "preview": "{\n  \"presets\": [\n    [\n      \"@babel/preset-env\",\n      {\n        \"loose\": true,\n        \"targets\": {\n          \"node\": "
  },
  {
    "path": ".flowconfig",
    "chars": 76,
    "preview": "[ignore]\ndist/.*\n\n[include]\n\n[libs]\n\n[options]\nesproposal.decorators=ignore\n"
  },
  {
    "path": ".gitignore",
    "chars": 61,
    "preview": ".idea\nnode_modules\ncoverage\ndist\nlib\nnpm-debug.log\n.DS_Store\n"
  },
  {
    "path": ".npmignore",
    "chars": 17,
    "preview": "src\ntest\nscripts\n"
  },
  {
    "path": ".travis.yml",
    "chars": 294,
    "preview": "sudo: false\nlanguage: node_js\nbefore_install:\n  - npm install -g npm@6.7.0\ncache:\n  directories:\n    - node_modules\nnoti"
  },
  {
    "path": "LICENSE",
    "chars": 1081,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2017 Erik Rasmussen\n\nPermission is hereby granted, free of charge, to any person ob"
  },
  {
    "path": "README.md",
    "chars": 1890,
    "preview": "# lru-memoize\n\n[![NPM Version](https://img.shields.io/npm/v/lru-memoize.svg?style=flat-square)](https://www.npmjs.com/pa"
  },
  {
    "path": "package-scripts.js",
    "chars": 2350,
    "preview": "const npsUtils = require(\"nps-utils\");\n\nconst series = npsUtils.series;\nconst concurrent = npsUtils.concurrent;\nconst ri"
  },
  {
    "path": "package.json",
    "chars": 2777,
    "preview": "{\n  \"name\": \"lru-memoize\",\n  \"version\": \"1.1.0\",\n  \"description\": \"A utility to provide lru memoization for any js funct"
  },
  {
    "path": "rollup.config.js",
    "chars": 2847,
    "preview": "import resolve from \"rollup-plugin-node-resolve\";\nimport babel from \"rollup-plugin-babel\";\nimport flow from \"rollup-plug"
  },
  {
    "path": "src/__tests__/deepEquals.spec.js",
    "chars": 3428,
    "preview": "import expect from 'expect'\nimport deepEquals from '../deepEquals'\n\nconst tripleEquals = deepEquals((valueA, valueB) => "
  },
  {
    "path": "src/__tests__/memoize.spec.js",
    "chars": 3750,
    "preview": "import expect from 'expect'\nimport memoize from '../memoize'\n\ndescribe('memoize', () => {\n  describe('basic', () => {\n  "
  },
  {
    "path": "src/deepEquals.js",
    "chars": 1725,
    "preview": "// @flow\nimport type { Equals } from \".\";\nconst hasOwn = (object, key) =>\n  Object.prototype.hasOwnProperty.call(object,"
  },
  {
    "path": "src/index.d.ts",
    "chars": 173,
    "preview": "declare const memoize = (\n  limit?: number,\n  equals?: (a: any, b: any) => boolean,\n  deepObjects?: boolean\n) => <T exte"
  },
  {
    "path": "src/index.js",
    "chars": 56,
    "preview": "import memoize from './memoize'\n\nexport default memoize\n"
  },
  {
    "path": "src/index.js.flow",
    "chars": 264,
    "preview": "// @flow\nexport type Entry = {\n  key: any,\n  value: any\n};\ntype ConfigItem = number | Function | boolean | void;\nexport "
  },
  {
    "path": "src/lruCache.js",
    "chars": 987,
    "preview": "// @flow\nimport type { Entry, Equals } from \".\";\n\nconst findIndex = (arr: any[], fn: any => any) => {\n  for (let i = 0; "
  },
  {
    "path": "src/memoize.js",
    "chars": 1802,
    "preview": "// @flow\nimport type { Config, Equals } from \".\";\nimport deepEquals from \"./deepEquals\";\nimport lruCache from \"./lruCach"
  },
  {
    "path": "src/singletonCache.js",
    "chars": 322,
    "preview": "// @flow\nimport type { Entry, Equals } from \".\";\n\nexport default function singletonCache(equals: Equals) {\n  let entry: "
  }
]

About this extraction

This page contains the full source code of the erikras/lru-memoize GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 19 files (24.0 KB), approximately 7.5k tokens, and a symbol index with 6 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!