Skip to content

Commit

Permalink
Update dependencies (#91)
Browse files Browse the repository at this point in the history
  • Loading branch information
fregante committed May 6, 2023
1 parent 542f8c5 commit 22c4b5e
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 90 deletions.
2 changes: 1 addition & 1 deletion .github/funding.yml
@@ -1,2 +1,2 @@
github: [sindresorhus, bfred-it]
github: [sindresorhus, fregante]
tidelift: npm/mem
8 changes: 4 additions & 4 deletions .github/workflows/main.yml
Expand Up @@ -10,12 +10,12 @@ jobs:
fail-fast: false
matrix:
node-version:
#- 20
- 18
- 16
- 14
- 12
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- run: npm install
Expand Down
16 changes: 8 additions & 8 deletions index.ts
Expand Up @@ -5,23 +5,23 @@ type AnyFunction = (...arguments_: any) => any;

const cacheStore = new WeakMap<AnyFunction, CacheStorage<any, any>>();

interface CacheStorageContent<ValueType> {
type CacheStorageContent<ValueType> = {
data: ValueType;
maxAge: number;
}
};

interface CacheStorage<KeyType, ValueType> {
type CacheStorage<KeyType, ValueType> = {
has: (key: KeyType) => boolean;
get: (key: KeyType) => CacheStorageContent<ValueType> | undefined;
set: (key: KeyType, value: CacheStorageContent<ValueType>) => void;
delete: (key: KeyType) => void;
clear?: () => void;
}
};

export interface Options<
export type Options<
FunctionToMemoize extends AnyFunction,
CacheKeyType,
> {
> = {
/**
Milliseconds until the cache expires.
Expand Down Expand Up @@ -63,7 +63,7 @@ export interface Options<
@example new WeakMap()
*/
readonly cache?: CacheStorage<CacheKeyType, ReturnType<FunctionToMemoize>>;
}
};

/**
[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.
Expand Down Expand Up @@ -174,7 +174,7 @@ export function memDecorator<
propertyKey: string,
descriptor: PropertyDescriptor,
): void => {
const input = target[propertyKey]; // eslint-disable-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
const input = target[propertyKey]; // eslint-disable-line @typescript-eslint/no-unsafe-assignment

if (typeof input !== 'function') {
throw new TypeError('The decorated value must be a function');
Expand Down
28 changes: 12 additions & 16 deletions package.json
Expand Up @@ -13,7 +13,7 @@
"type": "module",
"exports": "./dist/index.js",
"engines": {
"node": ">=12.20"
"node": ">=16"
},
"scripts": {
"test": "xo && ava && npm run build && tsd",
Expand All @@ -38,30 +38,26 @@
"promise"
],
"dependencies": {
"map-age-cleaner": "^0.1.3",
"map-age-cleaner": "^0.2.0",
"mimic-fn": "^4.0.0"
},
"devDependencies": {
"@ava/typescript": "^1.1.1",
"@sindresorhus/tsconfig": "^1.0.2",
"@types/serialize-javascript": "^4.0.0",
"ava": "^3.15.0",
"del-cli": "^3.0.1",
"delay": "^4.4.0",
"serialize-javascript": "^5.0.1",
"ts-node": "^10.1.0",
"tsd": "^0.13.1",
"typescript": "^4.3.5",
"xo": "^0.41.0"
"@sindresorhus/tsconfig": "^3.0.1",
"@types/serialize-javascript": "^5.0.2",
"ava": "^5.2.0",
"del-cli": "^5.0.0",
"delay": "^5.0.0",
"serialize-javascript": "^6.0.1",
"ts-node": "^10.9.1",
"tsd": "^0.28.1",
"typescript": "^5.0.4",
"xo": "^0.54.2"
},
"ava": {
"timeout": "1m",
"extensions": {
"ts": "module"
},
"nonSemVerExperiments": {
"configurableModuleFormat": true
},
"nodeArguments": [
"--loader=ts-node/esm"
]
Expand Down
18 changes: 10 additions & 8 deletions test-d/index.test-d.ts
@@ -1,6 +1,7 @@
import {expectType} from 'tsd';
import mem, {memClear} from '..';
import mem, {memClear} from '../index.js';

// eslint-disable-next-line unicorn/prefer-native-coercion-functions -- Required `string` type
const fn = (text: string) => Boolean(text);

expectType<typeof fn>(mem(fn));
Expand Down Expand Up @@ -33,42 +34,43 @@ memClear(fn);

// `cacheKey` tests.
// The argument should match the memoized function’s parameters
// eslint-disable-next-line unicorn/prefer-native-coercion-functions -- Required `string` type
mem((text: string) => Boolean(text), {
cacheKey: arguments_ => {
cacheKey(arguments_) {
expectType<[string]>(arguments_);
},
});

mem(() => 1, {
cacheKey: arguments_ => {
cacheKey(arguments_) {
expectType<[]>(arguments_); // eslint-disable-line @typescript-eslint/ban-types
},
});

// Ensures that the various cache functions infer their arguments type from the return type of `cacheKey`
mem((_arguments: {key: string}) => 1, {
cacheKey: (arguments_: [{key: string}]) => {
cacheKey(arguments_: [{key: string}]) {
expectType<[{key: string}]>(arguments_);
return new Date();
},
cache: {
get: key => {
get(key) {
expectType<Date>(key);

return {
data: 5,
maxAge: 2,
};
},
set: (key, data) => {
set(key, data) {
expectType<Date>(key);
expectType<{data: number; maxAge: number}>(data);
},
has: key => {
has(key) {
expectType<Date>(key);
return true;
},
delete: key => {
delete(key) {
expectType<Date>(key);
},
clear: () => undefined,
Expand Down
60 changes: 8 additions & 52 deletions test.ts
Expand Up @@ -5,48 +5,30 @@ import mem, {memDecorator, memClear} from './index.js';

test('memoize', t => {
let i = 0;
const fixture = () => i++;
const fixture = (a?: unknown, b?: unknown) => i++;
const memoized = mem(fixture);
t.is(memoized(), 0);
t.is(memoized(), 0);
t.is(memoized(), 0);
// @ts-expect-error
t.is(memoized(undefined), 0);
// @ts-expect-error
t.is(memoized(undefined), 0);
// @ts-expect-error
t.is(memoized('foo'), 1);
// @ts-expect-error
t.is(memoized('foo'), 1);
// @ts-expect-error
t.is(memoized('foo'), 1);
// @ts-expect-error
t.is(memoized('foo', 'bar'), 1);
// @ts-expect-error
t.is(memoized('foo', 'bar'), 1);
// @ts-expect-error
t.is(memoized('foo', 'bar'), 1);
// @ts-expect-error
t.is(memoized(1), 2);
// @ts-expect-error
t.is(memoized(1), 2);
// @ts-expect-error
t.is(memoized(null), 3);
// @ts-expect-error
t.is(memoized(null), 3);
// @ts-expect-error
t.is(memoized(fixture), 4);
// @ts-expect-error
t.is(memoized(fixture), 4);
// @ts-expect-error
t.is(memoized(true), 5);
// @ts-expect-error
t.is(memoized(true), 5);

// Ensure that functions are stored by reference and not by "value" (e.g. their `.toString()` representation)
// @ts-expect-error
t.is(memoized(() => i++), 6);
// @ts-expect-error
t.is(memoized(() => i++), 7);
});

Expand All @@ -63,70 +45,54 @@ test('cacheKey option', t => {

test('memoize with multiple non-primitive arguments', t => {
let i = 0;
const memoized = mem(() => i++, {cacheKey: JSON.stringify});
const memoized = mem((a?: unknown, b?: unknown, c?: unknown) => i++, {cacheKey: JSON.stringify});
t.is(memoized(), 0);
t.is(memoized(), 0);
// @ts-expect-error
t.is(memoized({foo: true}, {bar: false}), 1);
// @ts-expect-error
t.is(memoized({foo: true}, {bar: false}), 1);
// @ts-expect-error
t.is(memoized({foo: true}, {bar: false}, {baz: true}), 2);
// @ts-expect-error
t.is(memoized({foo: true}, {bar: false}, {baz: true}), 2);
});

test('memoize with regexp arguments', t => {
let i = 0;
const memoized = mem(() => i++, {cacheKey: serializeJavascript});
const memoized = mem((a?: unknown) => i++, {cacheKey: serializeJavascript});
t.is(memoized(), 0);
t.is(memoized(), 0);
// @ts-expect-error
t.is(memoized(/Sindre Sorhus/), 1);
// @ts-expect-error
t.is(memoized(/Sindre Sorhus/), 1);
// @ts-expect-error
t.is(memoized(/Elvin Peng/), 2);
// @ts-expect-error
t.is(memoized(/Elvin Peng/), 2);
});

test('memoize with Symbol arguments', t => {
let i = 0;
const argument1 = Symbol('fixture1');
const argument2 = Symbol('fixture2');
const memoized = mem(() => i++);
const memoized = mem((a?: unknown) => i++);
t.is(memoized(), 0);
t.is(memoized(), 0);
// @ts-expect-error
t.is(memoized(argument1), 1);
// @ts-expect-error
t.is(memoized(argument1), 1);
// @ts-expect-error
t.is(memoized(argument2), 2);
// @ts-expect-error
t.is(memoized(argument2), 2);
});

test('maxAge option', async t => {
let i = 0;
const fixture = () => i++;
const fixture = (a?: unknown) => i++;
const memoized = mem(fixture, {maxAge: 100});
// @ts-expect-error
t.is(memoized(1), 0);
// @ts-expect-error
t.is(memoized(1), 0);
await delay(50);
// @ts-expect-error
t.is(memoized(1), 0);
await delay(200);
// @ts-expect-error
t.is(memoized(1), 1);
});

test('maxAge option deletes old items', async t => {
let i = 0;
const fixture = () => i++;
const fixture = (a?: unknown) => i++;
const cache = new Map<number, number>();
const deleted: number[] = [];
const _delete = cache.delete.bind(cache);
Expand All @@ -135,27 +101,22 @@ test('maxAge option deletes old items', async t => {
return _delete(item);
};

// @ts-expect-error
const memoized = mem(fixture, {maxAge: 100, cache});
// @ts-expect-error
t.is(memoized(1), 0);
// @ts-expect-error
t.is(memoized(1), 0);
t.is(cache.has(1), true);
await delay(50);
// @ts-expect-error
t.is(memoized(1), 0);
t.is(deleted.length, 0);
await delay(200);
// @ts-expect-error
t.is(memoized(1), 1);
t.is(deleted.length, 1);
t.is(deleted[0], 1);
});

test('maxAge items are deleted even if function throws', async t => {
let i = 0;
const fixture = () => {
const fixture = (a?: unknown) => {
if (i === 1) {
throw new Error('failure');
}
Expand All @@ -165,17 +126,13 @@ test('maxAge items are deleted even if function throws', async t => {

const cache = new Map();
const memoized = mem(fixture, {maxAge: 100, cache});
// @ts-expect-error
t.is(memoized(1), 0);
// @ts-expect-error
t.is(memoized(1), 0);
t.is(cache.size, 1);
await delay(50);
// @ts-expect-error
t.is(memoized(1), 0);
await delay(200);
t.throws(() => {
// @ts-expect-error
memoized(1);
}, {message: 'failure'});
t.is(cache.size, 0);
Expand All @@ -198,10 +155,9 @@ test('cache option', t => {

test('promise support', async t => {
let i = 0;
const memoized = mem(async () => i++);
const memoized = mem(async (a?: unknown) => i++);
t.is(await memoized(), 0);
t.is(await memoized(), 0);
// @ts-expect-error
t.is(await memoized(10), 1);
});

Expand Down
5 changes: 4 additions & 1 deletion tsconfig.json
Expand Up @@ -6,5 +6,8 @@
},
"files": [
"index.ts"
]
],
"ts-node": {
"transpileOnly": true
}
}

0 comments on commit 22c4b5e

Please sign in to comment.