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

fix(Cache): typescript Map generic type fixed #308

Open
wants to merge 50 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
3d4784f
chore(Cache.apply): implement generic type
dimaslanjaka May 13, 2023
41137fd
fix: generic type
dimaslanjaka May 13, 2023
95492ad
chore: implement cache mapper
dimaslanjaka May 13, 2023
dfe4ddd
fix(TypeError): Cannot read property 'should' of undefined
dimaslanjaka May 13, 2023
8e32e9c
chore: restore old export style
dimaslanjaka May 13, 2023
7e54cf4
refactor: change cache import
dimaslanjaka May 13, 2023
0130bdb
chore: set cache size on `set` and `del` triggerred
dimaslanjaka May 13, 2023
3abcc5d
chore: make cache mapper prop to private
dimaslanjaka May 13, 2023
af55eba
chore: restore back `cache` property
dimaslanjaka May 13, 2023
e663b44
refactor: update imports
dimaslanjaka May 14, 2023
8cb3c0f
feat: set size while flusing cache
dimaslanjaka May 14, 2023
ae3780f
chore: moved `dump` into `Cache`
dimaslanjaka May 14, 2023
06d32c6
feat: implement typescript tests
dimaslanjaka May 14, 2023
c9b8997
refactor: update `dump` test
dimaslanjaka May 14, 2023
da25618
feat(Cache): add typescript test
dimaslanjaka May 14, 2023
fb6b487
refactor: rename test file
dimaslanjaka May 14, 2023
7943cfd
refactor: rename step
dimaslanjaka May 14, 2023
e239a39
feat: validate size after add same value with function
dimaslanjaka May 14, 2023
55ab91f
chore: split `CacheMapper`
dimaslanjaka May 14, 2023
d233f1b
feat: add `CacheType`
dimaslanjaka May 14, 2023
992a9fc
chore: separate `Cache` then import to `cache.ts`
dimaslanjaka May 14, 2023
287f2cb
fix: `Cache` private name exports
dimaslanjaka May 14, 2023
61dbdd1
refactor: cancel d233f1b
dimaslanjaka May 14, 2023
1ed3491
docs(apply): add JSDoc to each overload types
dimaslanjaka May 14, 2023
1ece3d5
docs: update JSDoc
dimaslanjaka May 14, 2023
cb3b0b3
docs: update JSDoc
dimaslanjaka May 14, 2023
0f416fe
chore: update JSDoc, fix invalid usage sample
dimaslanjaka May 14, 2023
5617efd
refactor: rename test cache number
dimaslanjaka May 15, 2023
b29c506
feat validate cache exist
dimaslanjaka May 15, 2023
5706883
feat: add object test
dimaslanjaka May 15, 2023
1553dcf
docs: update JSDoc usage
dimaslanjaka May 15, 2023
7147f60
docs: update JSDoc usage
dimaslanjaka May 15, 2023
d5b37ef
Merge branch 'fix-cache' of https://github.com/dimaslanjaka/hexo-util…
dimaslanjaka May 15, 2023
c2a8dbe
refactor: add test-single script
dimaslanjaka May 15, 2023
40b8a19
refactor: register test object & rename file test number
dimaslanjaka May 15, 2023
1622437
chore(CacheMapper): make `size` readonly modifier
dimaslanjaka May 15, 2023
1fd5511
chore: treat `CacheMapper` as internal
dimaslanjaka May 15, 2023
bb697bf
chore(CacheMapper): `_innerMap` using readonly modifier
dimaslanjaka May 15, 2023
41210cf
chore: detach `flush` from `CacheMapper`
dimaslanjaka May 15, 2023
2ba8147
chore: detach `apply` from `CacheMapper`
dimaslanjaka May 15, 2023
055391d
fix: Type 'V' is not assignable to 'V & (() => V)'
dimaslanjaka May 15, 2023
2cb48ad
fix: invalid map size
dimaslanjaka May 15, 2023
d984e19
docs: update `apply` JSDoc
dimaslanjaka May 15, 2023
db24238
refactor: update Cache object test
dimaslanjaka May 15, 2023
ddc98e3
refactor: disable typescript non-null assertion
dimaslanjaka May 15, 2023
6134ed2
feat: add built-in `Map` object test
dimaslanjaka May 15, 2023
9c40705
feat: add built-in `Set` object test
dimaslanjaka May 15, 2023
722f7d2
docs: update JSDoc
dimaslanjaka May 15, 2023
b9a3449
docs: update JSDoc
dimaslanjaka May 15, 2023
ddb89ec
Merge branch 'fix-cache' of https://github.com/dimaslanjaka/hexo-util…
dimaslanjaka May 15, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
161 changes: 161 additions & 0 deletions lib/CacheMapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/** @internal */
export class CacheMapper<K, V> implements Map<K, V> {
dimaslanjaka marked this conversation as resolved.
Show resolved Hide resolved
private readonly _innerMap: Map<K, V>;
readonly size: number;

constructor() {
this._innerMap = new Map();
}
clear(): void {
this._innerMap.clear();
}
delete(key: K): boolean {
throw this._innerMap.delete(key);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void {
return this._innerMap.forEach(callbackfn, thisArg);
}
entries(): IterableIterator<[K, V]> {
return this._innerMap.entries();
}
keys(): IterableIterator<K> {
return this._innerMap.keys();
}
values(): IterableIterator<V> {
return this._innerMap.values();
}
[Symbol.iterator](): IterableIterator<[K, V]> {
return this._innerMap.entries();
}
[Symbol.toStringTag]: string;

typeof() {
return typeof this._innerMap;
}

set(id: K, value: V) {
this._innerMap.set(id, value);
// set cache size while set new value
// this.size = this._innerMap.size;
return this;
}

has(id: K) {
return this._innerMap.has(id);
}

get(id: K) {
return this._innerMap.get(id);
}

del(id: K) {
this._innerMap.delete(id);
// set cache size while delete value
// this.size = this._innerMap.size;
}
}

/**
* Generic Mutable Cache with `Map`
* * A Map holds key-value pairs where the keys can be any datatype (Generic)
* @example
* import { Cache } from 'hexo-util';
* const c = new Cache<number>();
* // error
* c.set('key', 'xxxx'); // cache value must be instance of number
* // pass
* c.set('key', 1);
*/
export class Cache<V> {
private cache: CacheMapper<string, V>;
constructor() {
this.cache = new CacheMapper<string, V>();
}

/**
* check cache is exist with given key
* @param key cache key string
* @returns
*/
has(key: string) {
return this.cache.has(key);
}

/**
* get cache
* @param key
* @returns
*/
get(key: string) {
return this.cache.get(key);
}

/**
* set cache
* @param key
* @param value cache value must same as constructor generic type
* @returns
*/
set(key: string, value: V) {
return this.cache.set(key, value);
}

/**
* dump cache
* @returns
*/
dump() {
return Object.fromEntries(this.cache);
}

/**
* get cache total
* @returns
*/
size() {
return Array.from(this.cache.keys()).length;
}

/**
* cacheable setter non-function
* * new value will never updated when previous key already exist
* @param key cache key string
* @param value cache value must same as constructor generic type
*/
apply(key: string, value: V): V;

/**
* cacheable setter with function
* * new value will never updated when previous key already exist
* @param key cache key string
* @param value cache value must same as constructor generic type
*/
apply(key: string, value: () => V): V;

/**
* cacheable setter
* * new value will never updated when previous key already exist
* @param key cache key string
* @param value cache value must same as constructor generic type
*/
apply(id: string, value: V & (() => V)) {
if (this.has(id)) return this.get(id);
let newValue: V;
if (typeof value === 'function') {
newValue = value();
} else {
newValue = value;
}

this.cache.set(id, newValue);
return newValue;
}

del(key: string) {
return this.cache.del(key);
}
flush() {
this.cache.clear();
}
}
45 changes: 2 additions & 43 deletions lib/cache.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,3 @@
export = class Cache<T> {
cache: Map<string, T>;
import { Cache } from './CacheMapper';

constructor() {
this.cache = new Map();
}

set(id: string, value: T) {
this.cache.set(id, value);
}

has(id: string) {
return this.cache.has(id);
}

get(id: string) {
return this.cache.get(id);
}

del(id: string) {
this.cache.delete(id);
}

apply(id: string, value): T {
if (this.has(id)) return this.get(id);

if (typeof value === 'function') value = value();

this.set(id, value);
return value;
}

flush() {
this.cache.clear();
}

size() {
return this.cache.size;
}

dump() {
return Object.fromEntries(this.cache);
}
};
export = Cache;
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"pretest": "npm run clean && npm run build",
"test": "mocha --require ts-node/register",
"test-cov": "nyc --reporter=lcovonly npm run test",
"test-single": "npm run pretest && mocha --require ts-node/register --exit --grep",
"build:highlight": "node scripts/build_highlight_alias.js",
"postinstall": "npm run build:highlight"
},
Expand All @@ -34,7 +35,9 @@
],
"license": "MIT",
"devDependencies": {
"@types/chai": "^4.3.5",
"@types/cross-spawn": "^6.0.2",
"@types/mocha": "^10.0.1",
"@types/node": "^18.11.8",
"@types/prismjs": "^1.26.0",
"@typescript-eslint/eslint-plugin": "^5.41.0",
Expand Down
11 changes: 10 additions & 1 deletion test/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
{
"extends": "hexo/test",
"overrides": [
{
"files": ["*.ts"],
"rules": {
"node/no-unsupported-features/es-syntax": 0,
"@typescript-eslint/no-non-null-assertion": 0
}
}
],
"rules": {
"@typescript-eslint/no-var-requires": 0,
"@typescript-eslint/no-empty-function": 0
}
}
}
49 changes: 49 additions & 0 deletions test/cache-number.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
'use strict';

import { describe, it } from 'mocha';
import * as Hutil from '../lib';
import { expect } from 'chai';

// to run single test
// npm run test-single -- "Cache - number"

describe('Cache - number', () => {
const cache = new Hutil.Cache<number>();
const dumpExpect = { foo: 1, bar: 2 };

it('should be number', () => {
// apply non-function
expect(cache.apply('foo', 1)).to.be.an('number');
// apply with function
expect(cache.apply('bar', () => 2)).to.be.an('number');
// validate cache exist
expect(cache.has('foo') && cache.has('bar')).to.be.true;
});

it('add another and delete it', () => {
// add `another`
expect(cache.apply('another', 3)).to.equal(3);
// size should be 3
expect(cache.size()).to.equal(3);
// add with function
expect(cache.apply('another', () => 3)).to.equal(3);
// size should be still 3
expect(cache.size()).to.equal(3);
// delete `another`
cache.del('another');
});

it('final size should be 2', () => {
// final size should be 2
expect(cache.size()).to.equal(2);
});

it('should dump matches', () => {
expect(cache.dump()).deep.equal(dumpExpect);
});

it('should be empty after flush', () => {
cache.flush();
expect(cache.size()).to.be.equal(0);
});
});