diff --git a/src/commands.ts b/src/commands.ts index 065aede..83edb8b 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -136,15 +136,16 @@ const register: Command[] = []; function command(id: CommandId, options: { repository?: boolean } = {}) { return ( - target: CommandCenter, - key: unknown, - descriptor: TypedPropertyDescriptor - ): TypedPropertyDescriptor => { + fn: CommandMethod, + _context: ClassMethodDecoratorContext & { + name: CommandKey; + } + ) => { if (options.repository) { - descriptor.value = makeCommandWithRepository(descriptor.value!); + fn = makeCommandWithRepository(fn); } - register.push({ id, method: descriptor.value! }); - return descriptor; + register.push({ id, method: fn }); + return fn; }; } diff --git a/src/decorators.ts b/src/decorators.ts index 2a5286d..df461bf 100644 --- a/src/decorators.ts +++ b/src/decorators.ts @@ -4,109 +4,91 @@ * Licensed under the MIT License. See LICENSE.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import { done } from './util'; -function decorate( - decorator: (fn: Function, key: string) => Function -): Function { - return (target: any, key: string, descriptor: any) => { - let fnKey: string | null = null; - let fn: Function | null = null; - - if (typeof descriptor.value === 'function') { - fnKey = 'value'; - fn = descriptor.value; - } else if (typeof descriptor.get === 'function') { - fnKey = 'get'; - fn = descriptor.get; +export function memoize( + target: (this: Record) => Return, + context: ClassGetterDecoratorContext +) { + const memoizeKey = `$memoize$${context.name as string}`; + return function (this: Record): Return { + if (!this[memoizeKey]) { + this[memoizeKey] = target.apply(this); } - - if (!fn || !fnKey) { - throw new Error('not supported'); - } - - descriptor[fnKey] = decorator(fn, key); - }; -} - -function _memoize(fn: Function, key: string): Function { - const memoizeKey = `$memoize$${key}`; - - return function (this: Record, ...args: any[]) { - if (!this.hasOwnProperty(memoizeKey)) { - Object.defineProperty(this, memoizeKey, { - configurable: false, - enumerable: false, - writable: false, - value: fn.apply(this, args), - }); - } - return this[memoizeKey]; }; } - -export const memoize = decorate(_memoize); - /** * Decorator to not allow multiple async calls */ -function _throttle(fn: Function, key: string): Function { - const currentKey = `$throttle$current$${key}`; - const nextKey = `$throttle$next$${key}`; - - const trigger = function (this: Record, ...args: any[]) { +export function throttle( + target: ( + this: Record | undefined>, + ...args: Args + ) => Promise, + context: ClassMethodDecoratorContext +) { + const currentKey = `$thr$c$${String(context.name)}`; // $throttle$current$ + const nextKey = `$thr$n$${String(context.name)}`; // $throttle$next$ + + const trigger = function ( + this: Record | undefined>, + ...args: Args + ): Promise { if (this[nextKey]) { - return this[nextKey]; + return this[nextKey]!; } if (this[currentKey]) { - this[nextKey] = done(this[currentKey]).then(() => { + this[nextKey] = done(this[currentKey]!).then(() => { this[nextKey] = undefined; return trigger.apply(this, args); }); - return this[nextKey]; + return this[nextKey]!; } - this[currentKey] = fn.apply(this, args) as Promise; + this[currentKey] = target.apply(this, args); - done(this[currentKey]).then(() => { - this[currentKey] = undefined; - }); + const clear = () => (this[currentKey] = undefined); + done(this[currentKey]!).then(clear, clear); - return this[currentKey]; + return this[currentKey]!; }; return trigger; } -export const throttle = decorate(_throttle); +// Make sure asynchronous functions are called one after another. +type ThisPromise = Record>; -// Make sure asynchronous functions are called one after another (untested). -function _sequentialize(fn: Function, key: string): Function { - const currentKey = `__$sequence$${key}`; +export function sequentialize( + target: (this: ThisPromise, ...args: Args) => Promise, + context: ClassMethodDecoratorContext +) { + const currentKey = `$s11e$${context.name as string}`; // sequentialize - return function (this: any, ...args: any[]) { + return function (this: ThisPromise, ...args: Args): Promise { const currentPromise = (this[currentKey] as Promise) || Promise.resolve(null); - const run = async () => await fn.apply(this, args); + const run = async () => await target.apply(this, args); this[currentKey] = currentPromise.then(run, run); return this[currentKey]; }; } -export const sequentialize = decorate(_sequentialize); +type ThisTimer = Record>; -export function debounce(delay: number): Function { - return decorate((fn, key) => { - const timerKey = `$debounce$${key}`; +export function debounce(delay: number) { + return function ( + target: (this: ThisTimer, ...args: Args) => void, + context: ClassMemberDecoratorContext + ) { + const timerKey = `$d6e$${String(context.name)}`; // debounce - return function (this: Record, ...args: any[]) { + return function (this: ThisTimer, ...args: Args): void { clearTimeout(this[timerKey]); - this[timerKey] = setTimeout(() => fn.apply(this, args), delay); + this[timerKey] = setTimeout(() => target.apply(this, args), delay); }; - }); + }; } diff --git a/tsconfig.json b/tsconfig.json index 3edfa48..1b9a210 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,10 +1,9 @@ { "compileOnSave": true, "compilerOptions": { - "target": "es2018", + "target": "es2020", "lib": [ - "es6", - "es2018" + "es2020" ], "noImplicitOverride": true, "noImplicitThis": true, @@ -17,7 +16,6 @@ "removeComments": true, "sourceMap": true, "strictNullChecks": true, - "experimentalDecorators": true, }, "include": [ "src/**/*",