Skip to content
Open
Show file tree
Hide file tree
Changes from 27 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 163 additions & 0 deletions lib/CacheMapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
export class CacheMapper<K, V> implements Map<K, V> {
private _innerMap: Map<K, V>;
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;
}

apply(id: K, value: unknown) {
if (this.has(id)) return this.get(id);

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

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

flush() {
this._innerMap.clear();

// set cache size while flusing cache
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 this.cache.size;
}

/**
* apply cache non-function
* @param key cache key string
* @param value cache value must same as constructor generic type
*/
apply(key: string, value: V): V;

/**
* apply cache with function
* @param key cache key string
* @param value cache value must same as constructor generic type
*/
apply(key: string, value: () => V): V;

/**
* apply cache
* @param key cache key string
* @param value cache value must same as constructor generic type
*/
apply(key: string, value: (() => V) | V) {
return this.cache.apply(key, value);
}
del(key: string) {
return this.cache.del(key);
}
flush() {
return this.cache.flush();
}
}
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;
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,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
10 changes: 9 additions & 1 deletion test/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
{
"extends": "hexo/test",
"overrides": [
{
"files": ["*.ts"],
"rules": {
"node/no-unsupported-features/es-syntax": 0
}
}
],
"rules": {
"@typescript-eslint/no-var-requires": 0,
"@typescript-eslint/no-empty-function": 0
}
}
}
49 changes: 49 additions & 0 deletions test/cache.number.test.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
// mocha --require ts-node/register --exit --grep "Cache - Typescript"

describe('Cache - Typescript', () => {
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');
});

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);
});
});
});
57 changes: 34 additions & 23 deletions test/cache.spec.js
Original file line number Diff line number Diff line change
@@ -1,63 +1,74 @@
'use strict';

require('chai').should();
const chai = require('chai');
const should = chai.should();
const expect = chai.expect;

describe('Cache', () => {
// const Cache = require('../dist').Cache; // <-- this also works
const Cache = require('../dist/cache');
const cache = new Cache();

it('get & set', () => {
cache.set('foo', 123);
cache.get('foo').should.eql(123);
should.equal(cache.get('foo'), 123);
Copy link
Member

@SukkaW SukkaW May 14, 2023

Choose a reason for hiding this comment

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

@types/chat plus import {should} 'chat'; should(); should be enough to correct the type of [any object].prorotype.should, just like this: https://github.com/SukkaW/rollup-plugin-swc/blob/master/test/index.ts

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@types/chat plus import {should} 'chat'; should(); should be enough to correct the type of [any object].prorotype.should, just like this: https://github.com/SukkaW/rollup-plugin-swc/blob/master/test/index.ts

at my device using prototype.should getting error (throw undefined and make all tests failed), instead giving result test failure

Copy link
Contributor Author

@dimaslanjaka dimaslanjaka May 15, 2023

Choose a reason for hiding this comment

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

i dont know, why. But using normal chai API can give result failure correctly instead throw all tests runner, that was made me confused, because throw from node_modules paths instead from the tests files

Copy link
Contributor Author

@dimaslanjaka dimaslanjaka May 15, 2023

Choose a reason for hiding this comment

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

emm, i think this error caused by running on monorepo/workspace project. cuz all dependents grouped at single node_modules. but, this method easily to sync between packages. maybe, caused by version conflict (auto fixed by yarn), i dont know. Remembering all hexo packages has different dev dependencies versions.

Copy link
Contributor Author

@dimaslanjaka dimaslanjaka May 15, 2023

Choose a reason for hiding this comment

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

error says:

cannot read property 'should' of undefined

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sometimes got error like:

cannot read property 'eql' ... so on

Copy link
Contributor Author

@dimaslanjaka dimaslanjaka May 15, 2023

Choose a reason for hiding this comment

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

so to prevent useless error, prefer using original chai expect, should API for safety :)

});

it('size', () => {
cache.set('foobar', 456);
cache.size().should.eql(2);
should.equal(cache.size(), 2);
});

it('has', () => {
cache.has('foo').should.eql(true);
cache.has('bar').should.eql(false);
should.equal(cache.has('foo'), true);
should.equal(cache.has('bar'), false);
});

it('apply - non function', () => {
cache.apply('bar', 123).should.eql(123);
cache.apply('bar', 456).should.eql(123);

cache.apply('foo', 456).should.eql(123);
should.equal(cache.apply('bar', 123), 123);
should.equal(cache.apply('bar', 456), 123);
should.equal(cache.apply('foo', 456), 123);
});

it('apply - function', () => {
cache.apply('baz', () => 123).should.eql(123);
cache.apply('baz', () => 456).should.eql(123);
should.equal(
cache.apply('baz', () => 123),
123
);
should.equal(
cache.apply('baz', () => 456),
123
);
});

it('dump', () => {
cache.dump().should.eql({
'bar': 123,
'baz': 123,
'foo': 123,
'foobar': 456
expect(cache.dump()).to.include({
bar: 123,
baz: 123,
foo: 123,
foobar: 456
});
});

it('del', () => {
cache.del('baz');
cache.has('foo').should.eql(true);
cache.has('baz').should.eql(false);
should.equal(cache.has('foo'), true);
should.equal(cache.has('baz'), false);
});

it('flush', () => {
cache.flush();
cache.has('foo').should.eql(false);
cache.has('bar').should.eql(false);
cache.has('baz').should.eql(false);
cache.size().should.eql(0);
should.equal(cache.has('foo'), false);
should.equal(cache.has('bar'), false);
should.equal(cache.has('baz'), false);
should.equal(cache.size(), 0);
});

it('cache null', () => {
cache.apply('foo', null);
(cache.apply('foo', 123) === null).should.eql(true);
should.equal(cache.apply('foo', 123) === null, true);
});

// include typescript test
require('./cache.number.test.ts');
});