From 3d4784fd76122116f811792edb9b9dbee1d6839c Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Sat, 13 May 2023 23:57:25 +0700 Subject: [PATCH 01/46] chore(Cache.apply): implement generic type make `value` and `returns` as generic type --- lib/cache.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cache.ts b/lib/cache.ts index 4b0de14a..ca266958 100644 --- a/lib/cache.ts +++ b/lib/cache.ts @@ -21,7 +21,7 @@ export = class Cache { this.cache.delete(id); } - apply(id: string, value): T { + apply(id: string, value: T): T { if (this.has(id)) return this.get(id); if (typeof value === 'function') value = value(); From 41137fd8ee17154dd1ecd526488a8bb79f4762fe Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Sun, 14 May 2023 00:22:55 +0700 Subject: [PATCH 02/46] fix: generic type - disable @typescript-eslint/no-explicit-any to be able treat `value` as any - fixed failed build caused by `Not all constituents of type 'T | function' are callable` --- lib/cache.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/cache.ts b/lib/cache.ts index ca266958..ecf251db 100644 --- a/lib/cache.ts +++ b/lib/cache.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + export = class Cache { cache: Map; @@ -21,7 +23,7 @@ export = class Cache { this.cache.delete(id); } - apply(id: string, value: T): T { + apply(id: string, value: any): T { if (this.has(id)) return this.get(id); if (typeof value === 'function') value = value(); From 95492adafeeebe57a8809774cc3e1220c535f493 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Sun, 14 May 2023 02:41:41 +0700 Subject: [PATCH 03/46] chore: implement cache mapper cache now full support like built-in `Map` forked from: https://stackoverflow.com/a/68805082/6404439 --- lib/cache.ts | 86 ++++++++++++++++++++++++++++++++++++++-------- test/cache.spec.js | 10 +++--- 2 files changed, 76 insertions(+), 20 deletions(-) diff --git a/lib/cache.ts b/lib/cache.ts index ecf251db..74c174cb 100644 --- a/lib/cache.ts +++ b/lib/cache.ts @@ -1,46 +1,102 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ - -export = class Cache { - cache: Map; +class CacheMapper implements Map { + cache: Map; + size: number; constructor() { this.cache = new Map(); } + clear(): void { + this.cache.clear(); + } + delete(key: K): boolean { + throw this.cache.delete(key); + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + forEach(callbackfn: (value: V, key: K, map: Map) => void, thisArg?: any): void { + return this.cache.forEach(callbackfn, thisArg); + } + entries(): IterableIterator<[K, V]> { + return this.cache.entries(); + } + keys(): IterableIterator { + return this.cache.keys(); + } + values(): IterableIterator { + return this.cache.values(); + } + [Symbol.iterator](): IterableIterator<[K, V]> { + return this.cache.entries(); + } + [Symbol.toStringTag]: string; + + typeof() { + return typeof this.cache; + } - set(id: string, value: T) { + set(id: K, value: V) { this.cache.set(id, value); + return this; } - has(id: string) { + has(id: K) { return this.cache.has(id); } - get(id: string) { + get(id: K) { return this.cache.get(id); } - del(id: string) { + del(id: K) { this.cache.delete(id); } - apply(id: string, value: any): T { + apply(id: K, value: unknown) { if (this.has(id)) return this.get(id); if (typeof value === 'function') value = value(); - this.set(id, value); - return value; + this.set(id, value as V); + return value as V; } flush() { this.cache.clear(); } - size() { - return this.cache.size; + dump() { + return Object.fromEntries(this.cache); } +} +export default class Cache { + private _innerMap: CacheMapper; + constructor() { + this._innerMap = new CacheMapper(); + } + has(key: string) { + return this._innerMap.has(key); + } + get(key: string) { + return this._innerMap.get(key); + } + set(key: string, value:V) { + return this._innerMap.set(key, value); + } dump() { - return Object.fromEntries(this.cache); + return this._innerMap.dump(); + } + size() { + return this._innerMap.size; + } + apply(key: string, value: V): V; + apply(key: string, value: () => V): V; + apply(key: string, value: (() => V) | V) { + return this._innerMap.apply(key, value); + } + del(key: string) { + return this._innerMap.del(key); + } + flush() { + return this._innerMap.flush(); } -}; +} diff --git a/test/cache.spec.js b/test/cache.spec.js index 2864825a..f3fe81d4 100644 --- a/test/cache.spec.js +++ b/test/cache.spec.js @@ -3,7 +3,7 @@ require('chai').should(); describe('Cache', () => { - const Cache = require('../dist/cache'); + const { Cache } = require('../dist'); const cache = new Cache(); it('get & set', () => { @@ -35,10 +35,10 @@ describe('Cache', () => { it('dump', () => { cache.dump().should.eql({ - 'bar': 123, - 'baz': 123, - 'foo': 123, - 'foobar': 456 + bar: 123, + baz: 123, + foo: 123, + foobar: 456 }); }); From dfe4ddd9a76367a746138a47959f3a2655d17fbf Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Sun, 14 May 2023 03:05:04 +0700 Subject: [PATCH 04/46] fix(TypeError): Cannot read property 'should' of undefined --- test/cache.spec.js | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/test/cache.spec.js b/test/cache.spec.js index f3fe81d4..05335cb2 100644 --- a/test/cache.spec.js +++ b/test/cache.spec.js @@ -1,6 +1,6 @@ 'use strict'; -require('chai').should(); +const should = require('chai').should(); describe('Cache', () => { const { Cache } = require('../dist'); @@ -8,33 +8,32 @@ describe('Cache', () => { it('get & set', () => { cache.set('foo', 123); - cache.get('foo').should.eql(123); + should.equal(cache.get('foo'), 123); }); 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({ + should.equal(cache.dump(), { bar: 123, baz: 123, foo: 123, @@ -44,20 +43,20 @@ describe('Cache', () => { 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); }); }); From 8e32e9c484b27f87ec6ed69f75a7b849d464c331 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Sun, 14 May 2023 03:07:07 +0700 Subject: [PATCH 05/46] chore: restore old export style --- lib/cache.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/cache.ts b/lib/cache.ts index 74c174cb..068a7646 100644 --- a/lib/cache.ts +++ b/lib/cache.ts @@ -68,8 +68,8 @@ class CacheMapper implements Map { } } -export default class Cache { - private _innerMap: CacheMapper; +export = class Cache { + _innerMap: CacheMapper; constructor() { this._innerMap = new CacheMapper(); } From 7e54cf4ad66acfd885589ccbd36430e13be456d3 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Sun, 14 May 2023 03:11:23 +0700 Subject: [PATCH 06/46] refactor: change cache import --- test/cache.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cache.spec.js b/test/cache.spec.js index 05335cb2..dc356c65 100644 --- a/test/cache.spec.js +++ b/test/cache.spec.js @@ -3,7 +3,7 @@ const should = require('chai').should(); describe('Cache', () => { - const { Cache } = require('../dist'); + const Cache = require('../dist').Cache; const cache = new Cache(); it('get & set', () => { From 0130bdb8a19923d128b53cebda78b9261c55ffd3 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Sun, 14 May 2023 03:14:38 +0700 Subject: [PATCH 07/46] chore: set cache size on `set` and `del` triggerred --- lib/cache.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/cache.ts b/lib/cache.ts index 068a7646..5a795985 100644 --- a/lib/cache.ts +++ b/lib/cache.ts @@ -35,6 +35,8 @@ class CacheMapper implements Map { set(id: K, value: V) { this.cache.set(id, value); + // set cache size while set new value + this.size = this.cache.size; return this; } @@ -48,6 +50,8 @@ class CacheMapper implements Map { del(id: K) { this.cache.delete(id); + // set cache size while delete value + this.size = this.cache.size; } apply(id: K, value: unknown) { From 3abcc5d406a1391ee0580aca510ede6cc4f771fe Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Sun, 14 May 2023 03:16:39 +0700 Subject: [PATCH 08/46] chore: make cache mapper prop to private --- lib/cache.ts | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/cache.ts b/lib/cache.ts index 5a795985..1a537377 100644 --- a/lib/cache.ts +++ b/lib/cache.ts @@ -1,57 +1,57 @@ class CacheMapper implements Map { - cache: Map; + private _innerMap: Map; size: number; constructor() { - this.cache = new Map(); + this._innerMap = new Map(); } clear(): void { - this.cache.clear(); + this._innerMap.clear(); } delete(key: K): boolean { - throw this.cache.delete(key); + throw this._innerMap.delete(key); } // eslint-disable-next-line @typescript-eslint/no-explicit-any forEach(callbackfn: (value: V, key: K, map: Map) => void, thisArg?: any): void { - return this.cache.forEach(callbackfn, thisArg); + return this._innerMap.forEach(callbackfn, thisArg); } entries(): IterableIterator<[K, V]> { - return this.cache.entries(); + return this._innerMap.entries(); } keys(): IterableIterator { - return this.cache.keys(); + return this._innerMap.keys(); } values(): IterableIterator { - return this.cache.values(); + return this._innerMap.values(); } [Symbol.iterator](): IterableIterator<[K, V]> { - return this.cache.entries(); + return this._innerMap.entries(); } [Symbol.toStringTag]: string; typeof() { - return typeof this.cache; + return typeof this._innerMap; } set(id: K, value: V) { - this.cache.set(id, value); + this._innerMap.set(id, value); // set cache size while set new value - this.size = this.cache.size; + this.size = this._innerMap.size; return this; } has(id: K) { - return this.cache.has(id); + return this._innerMap.has(id); } get(id: K) { - return this.cache.get(id); + return this._innerMap.get(id); } del(id: K) { - this.cache.delete(id); + this._innerMap.delete(id); // set cache size while delete value - this.size = this.cache.size; + this.size = this._innerMap.size; } apply(id: K, value: unknown) { @@ -64,11 +64,11 @@ class CacheMapper implements Map { } flush() { - this.cache.clear(); + this._innerMap.clear(); } dump() { - return Object.fromEntries(this.cache); + return Object.fromEntries(this._innerMap); } } From af55eba4e6d4a635f3a0d523fa97bd7e5553cc14 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Sun, 14 May 2023 03:17:07 +0700 Subject: [PATCH 09/46] chore: restore back `cache` property --- lib/cache.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/cache.ts b/lib/cache.ts index 1a537377..9a198e71 100644 --- a/lib/cache.ts +++ b/lib/cache.ts @@ -73,34 +73,34 @@ class CacheMapper implements Map { } export = class Cache { - _innerMap: CacheMapper; + cache: CacheMapper; constructor() { - this._innerMap = new CacheMapper(); + this.cache = new CacheMapper(); } has(key: string) { - return this._innerMap.has(key); + return this.cache.has(key); } get(key: string) { - return this._innerMap.get(key); + return this.cache.get(key); } set(key: string, value:V) { - return this._innerMap.set(key, value); + return this.cache.set(key, value); } dump() { - return this._innerMap.dump(); + return this.cache.dump(); } size() { - return this._innerMap.size; + return this.cache.size; } apply(key: string, value: V): V; apply(key: string, value: () => V): V; apply(key: string, value: (() => V) | V) { - return this._innerMap.apply(key, value); + return this.cache.apply(key, value); } del(key: string) { - return this._innerMap.del(key); + return this.cache.del(key); } flush() { - return this._innerMap.flush(); + return this.cache.flush(); } } From e663b448e5058e819a2594994b0d0264b759ec22 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Sun, 14 May 2023 08:03:56 +0700 Subject: [PATCH 10/46] refactor: update imports --- test/cache.spec.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/cache.spec.js b/test/cache.spec.js index dc356c65..1a340aea 100644 --- a/test/cache.spec.js +++ b/test/cache.spec.js @@ -3,7 +3,8 @@ const should = require('chai').should(); describe('Cache', () => { - const Cache = require('../dist').Cache; + // const Cache = require('../dist').Cache; // <-- this also works + const Cache = require('../dist/cache'); const cache = new Cache(); it('get & set', () => { From 8cb3c0f51650b730cac6b63e05826413fda1548e Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Sun, 14 May 2023 08:10:57 +0700 Subject: [PATCH 11/46] feat: set size while flusing cache --- lib/cache.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/cache.ts b/lib/cache.ts index 9a198e71..f728dce7 100644 --- a/lib/cache.ts +++ b/lib/cache.ts @@ -65,6 +65,9 @@ class CacheMapper implements Map { flush() { this._innerMap.clear(); + + // set cache size while flusing cache + this.size = this._innerMap.size; } dump() { @@ -83,7 +86,7 @@ export = class Cache { get(key: string) { return this.cache.get(key); } - set(key: string, value:V) { + set(key: string, value: V) { return this.cache.set(key, value); } dump() { @@ -103,4 +106,4 @@ export = class Cache { flush() { return this.cache.flush(); } -} +}; From ae3780fa22ccefb4f0f73c716d0cafebb2029ff5 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Sun, 14 May 2023 08:14:03 +0700 Subject: [PATCH 12/46] chore: moved `dump` into `Cache` detach `dump` from CacheMapper --- lib/cache.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/cache.ts b/lib/cache.ts index f728dce7..3c5e0bd4 100644 --- a/lib/cache.ts +++ b/lib/cache.ts @@ -69,10 +69,6 @@ class CacheMapper implements Map { // set cache size while flusing cache this.size = this._innerMap.size; } - - dump() { - return Object.fromEntries(this._innerMap); - } } export = class Cache { @@ -90,7 +86,7 @@ export = class Cache { return this.cache.set(key, value); } dump() { - return this.cache.dump(); + return Object.fromEntries(this.cache); } size() { return this.cache.size; From 06d32c60f164fc001a7cbf73971d86289bcb9694 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Sun, 14 May 2023 08:25:57 +0700 Subject: [PATCH 13/46] feat: implement typescript tests chore(deps-dev): add `@types/chai` `@types/mocha` refactor(eslintrc): disable unsupported es-syntax for typescript --- package.json | 2 ++ test/.eslintrc.json | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 88c3b8ba..1aefcde4 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/test/.eslintrc.json b/test/.eslintrc.json index 26f258b3..0303aee8 100644 --- a/test/.eslintrc.json +++ b/test/.eslintrc.json @@ -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 } -} \ No newline at end of file +} From c9b8997374cc82bad1a3f3783e1e7bf4d1892268 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Sun, 14 May 2023 09:04:20 +0700 Subject: [PATCH 14/46] refactor: update `dump` test feat: import `chai.expect` fixed for objects not being sorted by NodeJS built-in `Map` --- test/cache.spec.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/test/cache.spec.js b/test/cache.spec.js index 1a340aea..95409021 100644 --- a/test/cache.spec.js +++ b/test/cache.spec.js @@ -1,6 +1,8 @@ 'use strict'; -const should = 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 @@ -29,12 +31,18 @@ describe('Cache', () => { }); it('apply - function', () => { - should.equal(cache.apply('baz', () => 123), 123); - should.equal(cache.apply('baz', () => 456), 123); + should.equal( + cache.apply('baz', () => 123), + 123 + ); + should.equal( + cache.apply('baz', () => 456), + 123 + ); }); it('dump', () => { - should.equal(cache.dump(), { + expect(cache.dump()).to.include({ bar: 123, baz: 123, foo: 123, From da25618e2c0adc58e5632417f1cd820f34f5cfd1 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Sun, 14 May 2023 09:10:27 +0700 Subject: [PATCH 15/46] feat(Cache): add typescript test --- test/cache.spec.js | 3 +++ test/cache.test.ts | 47 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 test/cache.test.ts diff --git a/test/cache.spec.js b/test/cache.spec.js index 95409021..dfaa5a6e 100644 --- a/test/cache.spec.js +++ b/test/cache.spec.js @@ -68,4 +68,7 @@ describe('Cache', () => { cache.apply('foo', null); should.equal(cache.apply('foo', 123) === null, true); }); + + // include typescript test + require('./cache.test.ts'); }); diff --git a/test/cache.test.ts b/test/cache.test.ts new file mode 100644 index 00000000..74fe0d77 --- /dev/null +++ b/test/cache.test.ts @@ -0,0 +1,47 @@ +'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(); + 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', () => { + // 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); + // 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); + }); + }); +}); From fb6b4873a2f557189941584fe0a5b9ff70ec2e95 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Sun, 14 May 2023 09:11:37 +0700 Subject: [PATCH 16/46] refactor: rename test file --- test/{cache.test.ts => cache.number.test.ts} | 0 test/cache.spec.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename test/{cache.test.ts => cache.number.test.ts} (100%) diff --git a/test/cache.test.ts b/test/cache.number.test.ts similarity index 100% rename from test/cache.test.ts rename to test/cache.number.test.ts diff --git a/test/cache.spec.js b/test/cache.spec.js index dfaa5a6e..859a9bcc 100644 --- a/test/cache.spec.js +++ b/test/cache.spec.js @@ -70,5 +70,5 @@ describe('Cache', () => { }); // include typescript test - require('./cache.test.ts'); + require('./cache.number.test.ts'); }); From 7943cfdaebe1e76034a0b9bfeda7e6c24decc399 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Sun, 14 May 2023 09:12:23 +0700 Subject: [PATCH 17/46] refactor: rename step --- test/cache.number.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cache.number.test.ts b/test/cache.number.test.ts index 74fe0d77..6a9d910c 100644 --- a/test/cache.number.test.ts +++ b/test/cache.number.test.ts @@ -19,7 +19,7 @@ describe('Cache - Typescript', () => { expect(cache.apply('bar', () => 2)).to.be.an('number'); }); - it('add another', () => { + it('add another and delete it', () => { // add `another` expect(cache.apply('another', 3)).to.equal(3); // size should be 3 From e239a390aa072b502c0527fe08e2462751ff30d5 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Sun, 14 May 2023 09:18:46 +0700 Subject: [PATCH 18/46] feat: validate size after add same value with function should be same `3` --- test/cache.number.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/cache.number.test.ts b/test/cache.number.test.ts index 6a9d910c..774b3239 100644 --- a/test/cache.number.test.ts +++ b/test/cache.number.test.ts @@ -26,6 +26,8 @@ describe('Cache - Typescript', () => { 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'); }); From 55ab91ffaad4aa8228a468a5706f307ecafd79d4 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Sun, 14 May 2023 09:28:19 +0700 Subject: [PATCH 19/46] chore: split `CacheMapper` fix: CacheMapper using private name --- lib/CacheMapper.ts | 72 +++++++++++++++++++++++++++++++++++++++++++++ lib/cache.ts | 73 +--------------------------------------------- 2 files changed, 73 insertions(+), 72 deletions(-) create mode 100644 lib/CacheMapper.ts diff --git a/lib/CacheMapper.ts b/lib/CacheMapper.ts new file mode 100644 index 00000000..9e631aa6 --- /dev/null +++ b/lib/CacheMapper.ts @@ -0,0 +1,72 @@ +export class CacheMapper implements Map { + private _innerMap: Map; + 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) => void, thisArg?: any): void { + return this._innerMap.forEach(callbackfn, thisArg); + } + entries(): IterableIterator<[K, V]> { + return this._innerMap.entries(); + } + keys(): IterableIterator { + return this._innerMap.keys(); + } + values(): IterableIterator { + 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; + } +} diff --git a/lib/cache.ts b/lib/cache.ts index 3c5e0bd4..bc4464ad 100644 --- a/lib/cache.ts +++ b/lib/cache.ts @@ -1,75 +1,4 @@ -class CacheMapper implements Map { - private _innerMap: Map; - 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) => void, thisArg?: any): void { - return this._innerMap.forEach(callbackfn, thisArg); - } - entries(): IterableIterator<[K, V]> { - return this._innerMap.entries(); - } - keys(): IterableIterator { - return this._innerMap.keys(); - } - values(): IterableIterator { - 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; - } -} +import { CacheMapper } from './CacheMapper'; export = class Cache { cache: CacheMapper; From d233f1bc9335b1928401a5ab26d7bea0eb3c609e Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Sun, 14 May 2023 14:56:39 +0700 Subject: [PATCH 20/46] feat: add `CacheType` to generify `Cache` sctructure --- lib/CacheMapper.ts | 15 +++++++++++++++ lib/cache.ts | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/CacheMapper.ts b/lib/CacheMapper.ts index 9e631aa6..ff4694a7 100644 --- a/lib/CacheMapper.ts +++ b/lib/CacheMapper.ts @@ -70,3 +70,18 @@ export class CacheMapper implements Map { this.size = this._innerMap.size; } } + +export interface CacheType { + cache: CacheMapper; + has(key: string): boolean; + get(key: string): V; + set(key: string, value: V): CacheMapper; + dump(): { + [k: string]: V; + }; + size(): number; + apply(key: string, value: V): V; + apply(key: string, value: () => V): V; + del(key: string): void; + flush(): void; +} diff --git a/lib/cache.ts b/lib/cache.ts index bc4464ad..67b1856e 100644 --- a/lib/cache.ts +++ b/lib/cache.ts @@ -1,6 +1,6 @@ -import { CacheMapper } from './CacheMapper'; +import { CacheMapper, CacheType } from './CacheMapper'; -export = class Cache { +export = class Cache implements CacheType { cache: CacheMapper; constructor() { this.cache = new CacheMapper(); From 992a9fcaa920f415b89a3c906803b3c3e23c72ac Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Sun, 14 May 2023 15:00:57 +0700 Subject: [PATCH 21/46] chore: separate `Cache` then import to `cache.ts` --- lib/CacheMapper.ts | 33 +++++++++++++++++++++++++++++++++ lib/cache.ts | 35 ++--------------------------------- 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/lib/CacheMapper.ts b/lib/CacheMapper.ts index 9e631aa6..64dc84ca 100644 --- a/lib/CacheMapper.ts +++ b/lib/CacheMapper.ts @@ -70,3 +70,36 @@ export class CacheMapper implements Map { this.size = this._innerMap.size; } } + +export class Cache { + cache: CacheMapper; + constructor() { + this.cache = new CacheMapper(); + } + has(key: string) { + return this.cache.has(key); + } + get(key: string) { + return this.cache.get(key); + } + set(key: string, value: V) { + return this.cache.set(key, value); + } + dump() { + return Object.fromEntries(this.cache); + } + size() { + return this.cache.size; + } + apply(key: string, value: V): V; + apply(key: string, value: () => V): V; + 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(); + } +} diff --git a/lib/cache.ts b/lib/cache.ts index bc4464ad..01164dff 100644 --- a/lib/cache.ts +++ b/lib/cache.ts @@ -1,34 +1,3 @@ -import { CacheMapper } from './CacheMapper'; +import { Cache } from './CacheMapper'; -export = class Cache { - cache: CacheMapper; - constructor() { - this.cache = new CacheMapper(); - } - has(key: string) { - return this.cache.has(key); - } - get(key: string) { - return this.cache.get(key); - } - set(key: string, value: V) { - return this.cache.set(key, value); - } - dump() { - return Object.fromEntries(this.cache); - } - size() { - return this.cache.size; - } - apply(key: string, value: V): V; - apply(key: string, value: () => V): V; - 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(); - } -}; +export = Cache; From 287f2cb90700a51023fe7afcc459bbeae1dea143 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Sun, 14 May 2023 15:03:19 +0700 Subject: [PATCH 22/46] fix: `Cache` private name exports hotifx Exported variable has or is using private name --- lib/CacheMapper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CacheMapper.ts b/lib/CacheMapper.ts index 64dc84ca..e5a5e26a 100644 --- a/lib/CacheMapper.ts +++ b/lib/CacheMapper.ts @@ -72,7 +72,7 @@ export class CacheMapper implements Map { } export class Cache { - cache: CacheMapper; + private cache: CacheMapper; constructor() { this.cache = new CacheMapper(); } From 1ed3491e6d7cd475958defd166f494eda61bca0a Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Sun, 14 May 2023 15:15:52 +0700 Subject: [PATCH 23/46] docs(apply): add JSDoc to each overload types --- lib/CacheMapper.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/CacheMapper.ts b/lib/CacheMapper.ts index e5a5e26a..b4dc6fc6 100644 --- a/lib/CacheMapper.ts +++ b/lib/CacheMapper.ts @@ -91,8 +91,26 @@ export class Cache { 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); } From 1ece3d5ab081a081037f760faf710f95d05abc3b Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Sun, 14 May 2023 15:19:51 +0700 Subject: [PATCH 24/46] docs: update JSDoc --- lib/CacheMapper.ts | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/lib/CacheMapper.ts b/lib/CacheMapper.ts index b4dc6fc6..6fe38890 100644 --- a/lib/CacheMapper.ts +++ b/lib/CacheMapper.ts @@ -71,23 +71,63 @@ export class CacheMapper implements Map { } } +/** + * 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; + * // error + * c.set('key', 'xxxx'); // cache value must be instance of number + * // pass + * c.set('key', 1); + */ export class Cache { private cache: CacheMapper; constructor() { this.cache = new CacheMapper(); } + + /** + * 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; } From cb3b0b3af57a3a64aaa3e8d46a43d7cc585f5de4 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Sun, 14 May 2023 15:20:14 +0700 Subject: [PATCH 25/46] docs: update JSDoc --- lib/CacheMapper.ts | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/lib/CacheMapper.ts b/lib/CacheMapper.ts index b4dc6fc6..db75aab0 100644 --- a/lib/CacheMapper.ts +++ b/lib/CacheMapper.ts @@ -71,23 +71,63 @@ export class CacheMapper implements Map { } } +/** + * 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(); + * // error + * c.set('key', 'xxxx'); // cache value must be instance of number + * // pass + * c.set('key', 1); + */ export class Cache { private cache: CacheMapper; constructor() { this.cache = new CacheMapper(); } + + /** + * 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; } From 5617efd7601fa520ff0f27ace934bb50499fcca6 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Mon, 15 May 2023 17:35:07 +0700 Subject: [PATCH 26/46] refactor: rename test cache number refactor: split cache typescript test --- test/cache-number.test.ts | 48 ++++++++++++++++++++++++++++++++++ test/cache-typescript.spec.ts | 10 +++++++ test/cache.number.test.ts | 49 ----------------------------------- test/cache.spec.js | 2 +- 4 files changed, 59 insertions(+), 50 deletions(-) create mode 100644 test/cache-number.test.ts create mode 100644 test/cache-typescript.spec.ts delete mode 100644 test/cache.number.test.ts diff --git a/test/cache-number.test.ts b/test/cache-number.test.ts new file mode 100644 index 00000000..51cb1740 --- /dev/null +++ b/test/cache-number.test.ts @@ -0,0 +1,48 @@ +'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 - number" + + +describe('Cache - number', () => { + const cache = new Hutil.Cache(); + 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); + }); +}); diff --git a/test/cache-typescript.spec.ts b/test/cache-typescript.spec.ts new file mode 100644 index 00000000..bb21dbaf --- /dev/null +++ b/test/cache-typescript.spec.ts @@ -0,0 +1,10 @@ +'use strict'; + +import { describe } from 'mocha'; + +// to run single test +// mocha --require ts-node/register --exit --grep "Cache - Typescript" + +describe('Cache - Typescript', () => { + import('./cache-number.test'); +}); diff --git a/test/cache.number.test.ts b/test/cache.number.test.ts deleted file mode 100644 index 774b3239..00000000 --- a/test/cache.number.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -'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(); - 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); - }); - }); -}); diff --git a/test/cache.spec.js b/test/cache.spec.js index 859a9bcc..29219e53 100644 --- a/test/cache.spec.js +++ b/test/cache.spec.js @@ -70,5 +70,5 @@ describe('Cache', () => { }); // include typescript test - require('./cache.number.test.ts'); + require('./cache-typescript.spec.ts'); }); From b29c506d488d235da5fe6b485e8acb443dae2146 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Mon, 15 May 2023 18:01:01 +0700 Subject: [PATCH 27/46] feat validate cache exist --- test/cache-number.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/cache-number.test.ts b/test/cache-number.test.ts index 51cb1740..a112e0a6 100644 --- a/test/cache-number.test.ts +++ b/test/cache-number.test.ts @@ -17,6 +17,8 @@ describe('Cache - number', () => { 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', () => { From 570688341e1260fc3b4d6b77a4e76a0af2871c69 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Mon, 15 May 2023 18:02:05 +0700 Subject: [PATCH 28/46] feat: add object test --- test/cache-object.spec.ts | 50 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 test/cache-object.spec.ts diff --git a/test/cache-object.spec.ts b/test/cache-object.spec.ts new file mode 100644 index 00000000..d6bb6907 --- /dev/null +++ b/test/cache-object.spec.ts @@ -0,0 +1,50 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +'use strict'; + +import { Cache } from '../lib'; +import { describe, it } from 'mocha'; +import { expect } from 'chai'; + +// to run single test +// mocha --require ts-node/register --exit --grep "Cache - Object" + +interface HexoLocals { + page: { + path: string; + }; + path: string; + url: string; + config: Record & { + relative_link: boolean; + }; + theme: Record; + layout: string; + env: any; + view_dir: string; + site: Record; + cache?: boolean; +} + +describe('Cache - object', () => { + const cache = new Cache(); + const value: HexoLocals = { + page: { + path: 'dummy/path/post.md' + }, + path: 'dummy/path/post.md', + url: 'http://example.com/', + config: { + relative_link: true + }, + theme: {}, + layout: '', + env: {}, + view_dir: '', + site: {} + }; + + it('apply cache', () => { + expect(cache.set('set', value)).to.deep.equal(value); + expect(cache.apply('apply', value)).to.deep.equal(value); + }); +}); From 1553dcf973b32d5fb15e407302a76a2b475f91de Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Mon, 15 May 2023 22:23:20 +0700 Subject: [PATCH 29/46] docs: update JSDoc usage --- test/cache-number.test.ts | 4 ++-- test/cache-object.spec.ts | 4 ++-- test/cache-typescript.spec.ts | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/cache-number.test.ts b/test/cache-number.test.ts index a112e0a6..fc91f2ab 100644 --- a/test/cache-number.test.ts +++ b/test/cache-number.test.ts @@ -4,8 +4,8 @@ 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 - number" +// to run single test (using yarn dlx https://yarnpkg.com/cli/dlx#examples) +// yarn dlx -p typescript -p ts-node -p chai -p mocha mocha --require ts-node/register --exit --grep "Cache - number" describe('Cache - number', () => { diff --git a/test/cache-object.spec.ts b/test/cache-object.spec.ts index d6bb6907..421a00a8 100644 --- a/test/cache-object.spec.ts +++ b/test/cache-object.spec.ts @@ -5,8 +5,8 @@ import { Cache } from '../lib'; import { describe, it } from 'mocha'; import { expect } from 'chai'; -// to run single test -// mocha --require ts-node/register --exit --grep "Cache - Object" +// to run single test (using yarn dlx https://yarnpkg.com/cli/dlx#examples) +// yarn dlx -p typescript -p ts-node -p chai -p mocha mocha --require ts-node/register --exit --grep "Cache - Object" interface HexoLocals { page: { diff --git a/test/cache-typescript.spec.ts b/test/cache-typescript.spec.ts index bb21dbaf..2f3784a9 100644 --- a/test/cache-typescript.spec.ts +++ b/test/cache-typescript.spec.ts @@ -2,9 +2,9 @@ import { describe } from 'mocha'; -// to run single test -// mocha --require ts-node/register --exit --grep "Cache - Typescript" +// to run single test (using yarn dlx https://yarnpkg.com/cli/dlx#examples) +// yarn dlx -p typescript -p ts-node -p chai -p mocha mocha --require ts-node/register --exit --grep "Cache - Typescript" -describe('Cache - Typescript', () => { - import('./cache-number.test'); +describe('Cache - Typescript', async () => { + await import('./cache-number.test'); }); From 7147f60c73751acc4bb62687a8dc9a917cf3e8db Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Mon, 15 May 2023 22:25:49 +0700 Subject: [PATCH 30/46] docs: update JSDoc usage --- test/cache-number.test.ts | 4 ++-- test/cache-object.spec.ts | 4 ++-- test/cache-typescript.spec.ts | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/cache-number.test.ts b/test/cache-number.test.ts index a112e0a6..fc91f2ab 100644 --- a/test/cache-number.test.ts +++ b/test/cache-number.test.ts @@ -4,8 +4,8 @@ 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 - number" +// to run single test (using yarn dlx https://yarnpkg.com/cli/dlx#examples) +// yarn dlx -p typescript -p ts-node -p chai -p mocha mocha --require ts-node/register --exit --grep "Cache - number" describe('Cache - number', () => { diff --git a/test/cache-object.spec.ts b/test/cache-object.spec.ts index d6bb6907..6f2da507 100644 --- a/test/cache-object.spec.ts +++ b/test/cache-object.spec.ts @@ -5,8 +5,8 @@ import { Cache } from '../lib'; import { describe, it } from 'mocha'; import { expect } from 'chai'; -// to run single test -// mocha --require ts-node/register --exit --grep "Cache - Object" +// to run single test (using yarn dlx https://yarnpkg.com/cli/dlx#examples) +// yarn dlx -p typescript -p ts-node -p chai -p mocha mocha --require ts-node/register --exit --grep "Cache - object" interface HexoLocals { page: { diff --git a/test/cache-typescript.spec.ts b/test/cache-typescript.spec.ts index bb21dbaf..2f3784a9 100644 --- a/test/cache-typescript.spec.ts +++ b/test/cache-typescript.spec.ts @@ -2,9 +2,9 @@ import { describe } from 'mocha'; -// to run single test -// mocha --require ts-node/register --exit --grep "Cache - Typescript" +// to run single test (using yarn dlx https://yarnpkg.com/cli/dlx#examples) +// yarn dlx -p typescript -p ts-node -p chai -p mocha mocha --require ts-node/register --exit --grep "Cache - Typescript" -describe('Cache - Typescript', () => { - import('./cache-number.test'); +describe('Cache - Typescript', async () => { + await import('./cache-number.test'); }); From c2a8dbe260f2eb4d7857e7f287e59a297639d25b Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Mon, 15 May 2023 22:32:39 +0700 Subject: [PATCH 31/46] refactor: add test-single script --- package.json | 1 + test/cache-number.test.ts | 5 ++--- test/cache-object.spec.ts | 4 ++-- test/cache-typescript.spec.ts | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 1aefcde4..1ceceea9 100644 --- a/package.json +++ b/package.json @@ -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" }, diff --git a/test/cache-number.test.ts b/test/cache-number.test.ts index fc91f2ab..eb991319 100644 --- a/test/cache-number.test.ts +++ b/test/cache-number.test.ts @@ -4,9 +4,8 @@ import { describe, it } from 'mocha'; import * as Hutil from '../lib'; import { expect } from 'chai'; -// to run single test (using yarn dlx https://yarnpkg.com/cli/dlx#examples) -// yarn dlx -p typescript -p ts-node -p chai -p mocha mocha --require ts-node/register --exit --grep "Cache - number" - +// to run single test +// npm run test-single -- "Cache - number" describe('Cache - number', () => { const cache = new Hutil.Cache(); diff --git a/test/cache-object.spec.ts b/test/cache-object.spec.ts index 6f2da507..ca9d92f3 100644 --- a/test/cache-object.spec.ts +++ b/test/cache-object.spec.ts @@ -5,8 +5,8 @@ import { Cache } from '../lib'; import { describe, it } from 'mocha'; import { expect } from 'chai'; -// to run single test (using yarn dlx https://yarnpkg.com/cli/dlx#examples) -// yarn dlx -p typescript -p ts-node -p chai -p mocha mocha --require ts-node/register --exit --grep "Cache - object" +// to run single test +// npm run test-single -- "Cache - object" interface HexoLocals { page: { diff --git a/test/cache-typescript.spec.ts b/test/cache-typescript.spec.ts index 2f3784a9..19b6633e 100644 --- a/test/cache-typescript.spec.ts +++ b/test/cache-typescript.spec.ts @@ -2,8 +2,8 @@ import { describe } from 'mocha'; -// to run single test (using yarn dlx https://yarnpkg.com/cli/dlx#examples) -// yarn dlx -p typescript -p ts-node -p chai -p mocha mocha --require ts-node/register --exit --grep "Cache - Typescript" +// to run single test +// npm run test-single -- "Cache - Typescript" describe('Cache - Typescript', async () => { await import('./cache-number.test'); From 40b8a19b913af19644f831388156bee2c4e7381b Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Mon, 15 May 2023 22:40:45 +0700 Subject: [PATCH 32/46] refactor: register test object & rename file test number --- test/{cache-number.test.ts => cache-number.spec.ts} | 0 test/cache-typescript.spec.ts | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) rename test/{cache-number.test.ts => cache-number.spec.ts} (100%) diff --git a/test/cache-number.test.ts b/test/cache-number.spec.ts similarity index 100% rename from test/cache-number.test.ts rename to test/cache-number.spec.ts diff --git a/test/cache-typescript.spec.ts b/test/cache-typescript.spec.ts index 19b6633e..ea97d58a 100644 --- a/test/cache-typescript.spec.ts +++ b/test/cache-typescript.spec.ts @@ -6,5 +6,6 @@ import { describe } from 'mocha'; // npm run test-single -- "Cache - Typescript" describe('Cache - Typescript', async () => { - await import('./cache-number.test'); + await import('./cache-number.spec'); + await import('./cache-object.spec'); }); From 1622437d8aa85fa8a4eb1962a7ee5d55ed8d508e Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Mon, 15 May 2023 22:53:50 +0700 Subject: [PATCH 33/46] chore(CacheMapper): make `size` readonly modifier chore(CacheMapper): drop `size` manipulator chore(Cache): get `CacheMapper` size by keys count --- lib/CacheMapper.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/CacheMapper.ts b/lib/CacheMapper.ts index db75aab0..f9a59a48 100644 --- a/lib/CacheMapper.ts +++ b/lib/CacheMapper.ts @@ -1,6 +1,6 @@ export class CacheMapper implements Map { private _innerMap: Map; - size: number; + readonly size: number; constructor() { this._innerMap = new Map(); @@ -36,7 +36,7 @@ export class CacheMapper implements Map { set(id: K, value: V) { this._innerMap.set(id, value); // set cache size while set new value - this.size = this._innerMap.size; + // this.size = this._innerMap.size; return this; } @@ -51,7 +51,7 @@ export class CacheMapper implements Map { del(id: K) { this._innerMap.delete(id); // set cache size while delete value - this.size = this._innerMap.size; + // this.size = this._innerMap.size; } apply(id: K, value: unknown) { @@ -67,7 +67,7 @@ export class CacheMapper implements Map { this._innerMap.clear(); // set cache size while flusing cache - this.size = this._innerMap.size; + // this.size = this._innerMap.size; } } @@ -129,7 +129,7 @@ export class Cache { * @returns */ size() { - return this.cache.size; + return Object.keys(this.cache).length; } /** From 1fd5511788e86a0288400726a93f35faf54e8055 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Mon, 15 May 2023 22:55:03 +0700 Subject: [PATCH 34/46] chore: treat `CacheMapper` as internal prevent exposed to index exports (VSCode, Intellij IDEA) --- lib/CacheMapper.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/CacheMapper.ts b/lib/CacheMapper.ts index f9a59a48..b5ac4026 100644 --- a/lib/CacheMapper.ts +++ b/lib/CacheMapper.ts @@ -1,3 +1,4 @@ +/** @internal */ export class CacheMapper implements Map { private _innerMap: Map; readonly size: number; From bb697bf64bfbb2c7854dc81a31d332e5215eac80 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Mon, 15 May 2023 22:57:33 +0700 Subject: [PATCH 35/46] chore(CacheMapper): `_innerMap` using readonly modifier --- lib/CacheMapper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CacheMapper.ts b/lib/CacheMapper.ts index b5ac4026..05789128 100644 --- a/lib/CacheMapper.ts +++ b/lib/CacheMapper.ts @@ -1,6 +1,6 @@ /** @internal */ export class CacheMapper implements Map { - private _innerMap: Map; + private readonly _innerMap: Map; readonly size: number; constructor() { From 41210cfe460aa6dacf636600e277cf3eb0316952 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Mon, 15 May 2023 23:26:17 +0700 Subject: [PATCH 36/46] chore: detach `flush` from `CacheMapper` --- lib/CacheMapper.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/CacheMapper.ts b/lib/CacheMapper.ts index 05789128..0c259500 100644 --- a/lib/CacheMapper.ts +++ b/lib/CacheMapper.ts @@ -63,13 +63,6 @@ export class CacheMapper implements Map { 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; - } } /** @@ -159,6 +152,6 @@ export class Cache { return this.cache.del(key); } flush() { - return this.cache.flush(); + this.cache.clear(); } } From 2ba81476c7b2981b2653e002fa10eba8fb01f2d9 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Mon, 15 May 2023 23:34:10 +0700 Subject: [PATCH 37/46] chore: detach `apply` from `CacheMapper` --- lib/CacheMapper.ts | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/lib/CacheMapper.ts b/lib/CacheMapper.ts index 0c259500..2c3765c9 100644 --- a/lib/CacheMapper.ts +++ b/lib/CacheMapper.ts @@ -54,15 +54,6 @@ export class CacheMapper implements Map { // 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; - } } /** @@ -145,9 +136,15 @@ export class 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); + apply(id: string, 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; } + del(key: string) { return this.cache.del(key); } From 055391d39bdb1114a0d632f2a0bc3b8c96f7560e Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Mon, 15 May 2023 23:45:34 +0700 Subject: [PATCH 38/46] fix: Type 'V' is not assignable to 'V & (() => V)' create new variable `newValue` to manipulate value parameter --- lib/CacheMapper.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/CacheMapper.ts b/lib/CacheMapper.ts index 2c3765c9..aeeffe34 100644 --- a/lib/CacheMapper.ts +++ b/lib/CacheMapper.ts @@ -136,13 +136,17 @@ export class Cache { * @param key cache key string * @param value cache value must same as constructor generic type */ - apply(id: string, value: unknown) { + apply(id: string, value: V & (() => V)) { if (this.has(id)) return this.get(id); - - if (typeof value === 'function') value = value(); - - this.set(id, value as V); - return value as V; + let newValue: V; + if (typeof value === 'function') { + newValue = value(); + } else { + newValue = value; + } + + this.cache.set(id, newValue); + return newValue; } del(key: string) { From 2cb48ad192ccd0c865d9e1fd606a56cf5a39df7e Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Mon, 15 May 2023 23:52:47 +0700 Subject: [PATCH 39/46] fix: invalid map size fixed by count the `Map.keys` length --- lib/CacheMapper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CacheMapper.ts b/lib/CacheMapper.ts index aeeffe34..92168bc8 100644 --- a/lib/CacheMapper.ts +++ b/lib/CacheMapper.ts @@ -114,7 +114,7 @@ export class Cache { * @returns */ size() { - return Object.keys(this.cache).length; + return Array.from(this.cache.keys()).length; } /** From d984e19bfc5d601693772710a142786ff272c47c Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Tue, 16 May 2023 00:04:47 +0700 Subject: [PATCH 40/46] docs: update `apply` JSDoc --- lib/CacheMapper.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/CacheMapper.ts b/lib/CacheMapper.ts index 92168bc8..c9cbc1dc 100644 --- a/lib/CacheMapper.ts +++ b/lib/CacheMapper.ts @@ -118,21 +118,24 @@ export class Cache { } /** - * apply cache non-function + * 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; /** - * apply cache with function + * 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; /** - * apply cache + * 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 */ From db242386395960c7ce3f3d58e432beb8010c3aed Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Tue, 16 May 2023 00:09:51 +0700 Subject: [PATCH 41/46] refactor: update Cache object test --- test/cache-object.spec.ts | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/test/cache-object.spec.ts b/test/cache-object.spec.ts index ca9d92f3..f1ad14f1 100644 --- a/test/cache-object.spec.ts +++ b/test/cache-object.spec.ts @@ -3,7 +3,7 @@ import { Cache } from '../lib'; import { describe, it } from 'mocha'; -import { expect } from 'chai'; +import { expect, should } from 'chai'; // to run single test // npm run test-single -- "Cache - object" @@ -43,8 +43,22 @@ describe('Cache - object', () => { site: {} }; - it('apply cache', () => { - expect(cache.set('set', value)).to.deep.equal(value); - expect(cache.apply('apply', value)).to.deep.equal(value); + it('set', () => { + cache.set('foo', value); + should().equal(cache.size(), 1); + }); + + it('apply', () => { + expect(cache.has('bar')).to.be.false; + // should applied and return the same value + should().equal(cache.apply('bar', value), value); + // should not apply new value + should().equal(cache.apply('bar', {} as typeof value), value); + should().equal(cache.size(), 2); + }); + + it('size 0 after flush', () => { + cache.flush(); + expect(cache.size()).to.equal(0); }); }); From ddc98e3da899f53706b90b6d99ecd54975ef91ae Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Tue, 16 May 2023 00:33:22 +0700 Subject: [PATCH 42/46] refactor: disable typescript non-null assertion --- test/.eslintrc.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/.eslintrc.json b/test/.eslintrc.json index 0303aee8..db9548e4 100644 --- a/test/.eslintrc.json +++ b/test/.eslintrc.json @@ -4,7 +4,8 @@ { "files": ["*.ts"], "rules": { - "node/no-unsupported-features/es-syntax": 0 + "node/no-unsupported-features/es-syntax": 0, + "@typescript-eslint/no-non-null-assertion": 0 } } ], From 6134ed20f68149dc75f82147b627d91cbbc23604 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Tue, 16 May 2023 00:37:45 +0700 Subject: [PATCH 43/46] feat: add built-in `Map` object test --- test/cache-object.spec.ts | 46 ++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/test/cache-object.spec.ts b/test/cache-object.spec.ts index f1ad14f1..03de0646 100644 --- a/test/cache-object.spec.ts +++ b/test/cache-object.spec.ts @@ -42,23 +42,49 @@ describe('Cache - object', () => { view_dir: '', site: {} }; + const valueMap = new Map(Object.entries(value)); + const cacheMap = new Cache(); - it('set', () => { - cache.set('foo', value); - should().equal(cache.size(), 1); + describe('plain object', () => { + it('set', () => { + cache.set('foo', value); + should().equal(cache.size(), 1); + }); + + it('apply', () => { + expect(cache.has('bar')).to.be.false; + // should applied and return the same value + should().equal(cache.apply('bar', value), value); + // should not apply new value + should().equal(cache.apply('bar', {} as typeof value), value); + should().equal(cache.size(), 2); + }); }); - it('apply', () => { - expect(cache.has('bar')).to.be.false; - // should applied and return the same value - should().equal(cache.apply('bar', value), value); - // should not apply new value - should().equal(cache.apply('bar', {} as typeof value), value); - should().equal(cache.size(), 2); + describe('map object', () => { + it('set', () => { + cacheMap.set('foo', valueMap); + should().equal(cacheMap.size(), 1); + }); + it('apply', () => { + // should applied and return the same value + should().equal(cacheMap.apply('bar', valueMap), valueMap); + }); + it('is valid map', () => { + const targetValue = cacheMap.get('bar'); + // built-in Map validate + expect(targetValue! instanceof Map).to.be.true; + expect('has' in targetValue!).to.be.true; + expect(targetValue?.has('page')).to.be.true; + // targetValue.page should same as value.page + should().equal(targetValue?.get('page'), value.page); + }); }); it('size 0 after flush', () => { cache.flush(); expect(cache.size()).to.equal(0); + cacheMap.flush(); + expect(cacheMap.size()).to.equal(0); }); }); From 9c40705e652961fe1069179452bfabe15d25ad17 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Tue, 16 May 2023 00:42:30 +0700 Subject: [PATCH 44/46] feat: add built-in `Set` object test --- test/cache-object.spec.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/test/cache-object.spec.ts b/test/cache-object.spec.ts index 03de0646..a2c7a6c0 100644 --- a/test/cache-object.spec.ts +++ b/test/cache-object.spec.ts @@ -44,6 +44,8 @@ describe('Cache - object', () => { }; const valueMap = new Map(Object.entries(value)); const cacheMap = new Cache(); + const valueSet = new Set(Object.keys(value)); + const cacheSet = new Cache(); describe('plain object', () => { it('set', () => { @@ -61,7 +63,7 @@ describe('Cache - object', () => { }); }); - describe('map object', () => { + describe('object Map', () => { it('set', () => { cacheMap.set('foo', valueMap); should().equal(cacheMap.size(), 1); @@ -81,6 +83,23 @@ describe('Cache - object', () => { }); }); + describe('object Set', () => { + it('set', () => { + cacheSet.set('foo', valueSet); + should().equal(cacheSet.size(), 1); + }); + it('apply', () => { + // should applied and return the same value + should().equal(cacheSet.apply('bar', valueSet), valueSet); + }); + it('is valid map', () => { + const targetValue = cacheSet.get('bar'); + // built-in Map validate + expect(targetValue! instanceof Set).to.be.true; + expect(targetValue?.has('page')).to.be.true; + }); + }); + it('size 0 after flush', () => { cache.flush(); expect(cache.size()).to.equal(0); From 722f7d254ae14752f7f058cac9012720b00ae0c7 Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Tue, 16 May 2023 00:43:12 +0700 Subject: [PATCH 45/46] docs: update JSDoc --- test/cache-object.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/cache-object.spec.ts b/test/cache-object.spec.ts index a2c7a6c0..5be8b90e 100644 --- a/test/cache-object.spec.ts +++ b/test/cache-object.spec.ts @@ -44,6 +44,7 @@ describe('Cache - object', () => { }; const valueMap = new Map(Object.entries(value)); const cacheMap = new Cache(); + // built-in Set same as Array const valueSet = new Set(Object.keys(value)); const cacheSet = new Cache(); From b9a34492354b3344d51c464367e417abacafd2eb Mon Sep 17 00:00:00 2001 From: dimaslanjaka Date: Tue, 16 May 2023 00:44:08 +0700 Subject: [PATCH 46/46] docs: update JSDoc --- test/cache-object.spec.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/cache-object.spec.ts b/test/cache-object.spec.ts index a2c7a6c0..674f6d10 100644 --- a/test/cache-object.spec.ts +++ b/test/cache-object.spec.ts @@ -44,6 +44,7 @@ describe('Cache - object', () => { }; const valueMap = new Map(Object.entries(value)); const cacheMap = new Cache(); + // built-in Set same as Array const valueSet = new Set(Object.keys(value)); const cacheSet = new Cache(); @@ -72,7 +73,7 @@ describe('Cache - object', () => { // should applied and return the same value should().equal(cacheMap.apply('bar', valueMap), valueMap); }); - it('is valid map', () => { + it('is valid Map', () => { const targetValue = cacheMap.get('bar'); // built-in Map validate expect(targetValue! instanceof Map).to.be.true; @@ -92,7 +93,7 @@ describe('Cache - object', () => { // should applied and return the same value should().equal(cacheSet.apply('bar', valueSet), valueSet); }); - it('is valid map', () => { + it('is valid Set', () => { const targetValue = cacheSet.get('bar'); // built-in Map validate expect(targetValue! instanceof Set).to.be.true;