Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Require Node.js 12.20 and move to ESM #76

Merged
merged 6 commits into from
Jul 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ jobs:
fail-fast: false
matrix:
node-version:
- 16
- 14
- 12
- 10
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
- uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
- run: npm install
Expand Down
107 changes: 52 additions & 55 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
'use strict';
import mimicFn = require('mimic-fn');
import mapAgeCleaner = require('map-age-cleaner');
import mimicFn from 'mimic-fn';
import mapAgeCleaner from 'map-age-cleaner';

type AnyFunction = (...arguments_: any) => any;

const decoratorInstanceMap = new WeakMap();

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

interface CacheStorageContent<ValueType> {
data: ValueType;
Expand All @@ -23,7 +22,7 @@ interface CacheStorage<KeyType, ValueType> {

interface Options<
FunctionToMemoize extends AnyFunction,
CacheKeyType
CacheKeyType,
> {
/**
Milliseconds until the cache expires.
Expand All @@ -40,16 +39,16 @@ interface Options<
You can have it cache **all** the arguments by value with `JSON.stringify`, if they are compatible:

```
import mem = require('mem');
import mem from 'mem';

mem(function_, {cacheKey: JSON.stringify});
```

Or you can use a more full-featured serializer like [serialize-javascript](https://github.com/yahoo/serialize-javascript) to add support for `RegExp`, `Date` and so on.

```
import mem = require('mem');
import serializeJavascript = require('serialize-javascript');
import mem from 'mem';
import serializeJavascript from 'serialize-javascript';

mem(function_, {cacheKey: serializeJavascript});
```
Expand All @@ -75,7 +74,7 @@ interface Options<

@example
```
import mem = require('mem');
import mem from 'mem';

let i = 0;
const counter = () => ++i;
Expand All @@ -96,63 +95,59 @@ memoized('bar');
//=> 2
```
*/
const mem = <
export default function mem<
FunctionToMemoize extends AnyFunction,
CacheKeyType
CacheKeyType,
>(
fn: FunctionToMemoize,
{
cacheKey,
cache = new Map(),
maxAge
}: Options<FunctionToMemoize, CacheKeyType> = {}
): FunctionToMemoize => {
maxAge,
}: Options<FunctionToMemoize, CacheKeyType> = {},
): FunctionToMemoize {
if (typeof maxAge === 'number') {
// TODO: Drop after https://github.com/SamVerschueren/map-age-cleaner/issues/5
// @ts-expect-error
mapAgeCleaner(cache);
mapAgeCleaner(cache as unknown as Map<CacheKeyType, ReturnType<FunctionToMemoize>>);
}

const memoized = function (this: any, ...arguments_) {
const key = cacheKey ? cacheKey(arguments_) : arguments_[0];
const memoized = function (this: any, ...arguments_: Parameters<FunctionToMemoize>): ReturnType<FunctionToMemoize> {
const key = cacheKey ? cacheKey(arguments_) : arguments_[0] as CacheKeyType;

const cacheItem = cache.get(key);
if (cacheItem) {
return cacheItem.data;
return cacheItem.data; // eslint-disable-line @typescript-eslint/no-unsafe-return
Copy link
Contributor Author

@Richienb Richienb Jul 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the return value of AnyFunction is any and FunctionToMemoize extends AnyFunction, the linter assumes that ReturnType<FunctionToMemoize> is any. However, the error is that any is not assignable to ReturnType<FunctionToMemoize>:

✖ 118:4 Unsafe return of type any from function with return type ReturnType<FunctionToMemoize>. @typescript-eslint/no-unsafe-return

This might be a bug with typescript-eslint.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be a bug with typescript-eslint.

Open an issue ;)

}

const result = fn.apply(this, arguments_);
const result = fn.apply(this, arguments_) as ReturnType<FunctionToMemoize>;

cache.set(key, {
data: result,
maxAge: maxAge ? Date.now() + maxAge : Number.POSITIVE_INFINITY
maxAge: maxAge ? Date.now() + maxAge : Number.POSITIVE_INFINITY,
});

return result;
return result; // eslint-disable-line @typescript-eslint/no-unsafe-return
} as FunctionToMemoize;

mimicFn(memoized, fn, {
ignoreNonConfigurable: true
ignoreNonConfigurable: true,
});

cacheStore.set(memoized, cache);

return memoized;
};

export = mem;
}

/**
@returns A [decorator](https://github.com/tc39/proposal-decorators) to memoize class methods or static class methods.

@example
```
import mem = require('mem');
import {memDecorator} from 'mem';

class Example {
index = 0

@mem.decorator()
@memDecorator()
counter() {
return ++this.index;
}
Expand All @@ -161,49 +156,51 @@ class Example {
class ExampleWithOptions {
index = 0

@mem.decorator({maxAge: 1000})
@memDecorator({maxAge: 1000})
counter() {
return ++this.index;
}
}
```
*/
mem.decorator = <
export function memDecorator<
FunctionToMemoize extends AnyFunction,
CacheKeyType
CacheKeyType,
>(
options: Options<FunctionToMemoize, CacheKeyType> = {}
) => (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
): void => {
const input = target[propertyKey];

if (typeof input !== 'function') {
throw new TypeError('The decorated value must be a function');
}
options: Options<FunctionToMemoize, CacheKeyType> = {},
) {
return (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor,
): void => {
const input = target[propertyKey]; // eslint-disable-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These errors must be ignored because target: any and there is currently no viable alternative.
https://stackoverflow.com/questions/68343539/typescript-definition-of-target-in-a-decorator


if (typeof input !== 'function') {
throw new TypeError('The decorated value must be a function');
}

delete descriptor.value;
delete descriptor.writable;
delete descriptor.value;
delete descriptor.writable;

descriptor.get = function () {
if (!decoratorInstanceMap.has(this)) {
const value = mem(input, options);
decoratorInstanceMap.set(this, value);
return value;
}
descriptor.get = function () {
if (!decoratorInstanceMap.has(this)) {
const value = mem(input, options) as FunctionToMemoize;
decoratorInstanceMap.set(this, value);
return value;
}

return decoratorInstanceMap.get(this);
return decoratorInstanceMap.get(this) as FunctionToMemoize;
};
};
};
}

/**
Clear all cached data of a memoized function.

@param fn - Memoized function.
*/
mem.clear = (fn: AnyFunction): void => {
export function memClear(fn: AnyFunction): void {
const cache = cacheStore.get(fn);
if (!cache) {
throw new TypeError('Can\'t clear a function that was not memoized!');
Expand All @@ -214,4 +211,4 @@ mem.clear = (fn: AnyFunction): void => {
}

cache.clear();
};
}
36 changes: 19 additions & 17 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@
"email": "sindresorhus@gmail.com",
"url": "https://sindresorhus.com"
},
"type": "module",
"exports": "./dist/index.js",
"engines": {
"node": ">=10"
"node": ">=12.20"
},
"scripts": {
"test": "xo && npm run build && tsd && ava",
"test": "xo && ava && npm run build && tsd",
"build": "del-cli dist && tsc",
"prepack": "npm run build"
},
"main": "dist",
"types": "dist/index.d.ts",
"files": [
"dist/index.js",
"dist/index.d.ts"
"dist"
],
"keywords": [
"memoize",
Expand All @@ -39,30 +39,32 @@
],
"dependencies": {
"map-age-cleaner": "^0.1.3",
"mimic-fn": "^3.1.0"
"mimic-fn": "^4.0.0"
},
"devDependencies": {
"@ava/typescript": "^1.1.1",
"@sindresorhus/tsconfig": "^0.7.0",
"@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.0.3",
"xo": "^0.38.2"
"typescript": "^4.3.5",
"xo": "^0.41.0"
},
"ava": {
"files": [
"test.ts"
],
"timeout": "1m",
"typescript": {
"rewritePaths": {
"./": "dist/"
}
}
"extensions": {
"ts": "module"
},
"nonSemVerExperiments": {
"configurableModuleFormat": true
},
"nodeArguments": [
"--loader=ts-node/esm"
]
},
"xo": {
"rules": {
Expand Down
Loading