diff --git a/src/system.ts b/src/system.ts index e5e18db..b036194 100644 --- a/src/system.ts +++ b/src/system.ts @@ -23,6 +23,7 @@ export * as Dates from './system/date'; export * from './system/decorators/gate'; export * from './system/decorators/log'; export * from './system/decorators/memoize'; +export * from './system/decorators/serialize'; export * from './system/decorators/timeout'; export * as Encoding from './system/encoding'; export * as Functions from './system/function'; diff --git a/src/system/decorators/gate.ts b/src/system/decorators/gate.ts index 718ff67..331a1e9 100644 --- a/src/system/decorators/gate.ts +++ b/src/system/decorators/gate.ts @@ -1,4 +1,5 @@ 'use strict'; +import { Uri } from 'vscode'; import { is as isPromise } from '../promise'; const emptyStr = ''; @@ -8,9 +9,8 @@ function defaultResolver(...args: any[]): string { const arg0 = args[0]; if (arg0 == null) return emptyStr; if (typeof arg0 === 'string') return arg0; - if (typeof arg0 === 'number' || typeof arg0 === 'boolean') { - return String(arg0); - } + if (typeof arg0 === 'number' || typeof arg0 === 'boolean' || arg0 instanceof Error) return String(arg0); + if (arg0 instanceof Uri) return arg0.toString(); return JSON.stringify(arg0); } diff --git a/src/system/decorators/serialize.ts b/src/system/decorators/serialize.ts new file mode 100644 index 0000000..99262f4 --- /dev/null +++ b/src/system/decorators/serialize.ts @@ -0,0 +1,61 @@ +'use strict'; +import { Uri } from 'vscode'; + +const emptyStr = ''; + +function defaultResolver(...args: any[]): string { + if (args.length === 1) { + const arg0 = args[0]; + if (arg0 === undefined) return emptyStr; + if (typeof arg0 === 'string') return arg0; + if (typeof arg0 === 'number' || typeof arg0 === 'boolean' || arg0 instanceof Error) return String(arg0); + if (arg0 instanceof Uri) return arg0.toString(); + + return JSON.stringify(arg0); + } + + return JSON.stringify(args); +} + +export function serialize any>( + resolver?: (...args: Parameters) => string, +): (target: any, key: string, descriptor: PropertyDescriptor) => void { + return (target: any, key: string, descriptor: PropertyDescriptor) => { + let fn: Function | undefined; + if (typeof descriptor.value === 'function') { + fn = descriptor.value; + } else if (typeof descriptor.get === 'function') { + fn = descriptor.get; + } + if (fn === undefined) throw new Error('Not supported'); + + const serializeKey = `$serialize$${key}`; + + descriptor.value = function (this: any, ...args: any[]) { + const prop = + args.length === 0 + ? serializeKey + : `${serializeKey}$${(resolver ?? defaultResolver)(...(args as Parameters))}`; + + if (!Object.prototype.hasOwnProperty.call(this, prop)) { + Object.defineProperty(this, prop, { + configurable: false, + enumerable: false, + writable: true, + value: undefined, + }); + } + + let promise = this[prop]; + const run = () => fn!.apply(this, args); + if (promise === undefined) { + promise = run(); + } else { + promise = promise.then(run, run); + } + + this[prop] = promise; + return promise; + }; + }; +}