diff --git a/src/ts/@overflow/commons/core/type/metadata.ts b/src/ts/@overflow/commons/core/type/metadata.ts index 5192301..ba5540b 100644 --- a/src/ts/@overflow/commons/core/type/metadata.ts +++ b/src/ts/@overflow/commons/core/type/metadata.ts @@ -1,8 +1,9 @@ import { + MetadataKeyType, PropertyKeyType, } from './type'; -import * as Util from './util'; +import {TypeUtil} from './util'; export class Metadata { @@ -41,8 +42,8 @@ export class Metadata { * ``` * */ - public static get(key: string, target: any, propertyKey?: PropertyKeyType): any { - return Reflect.getMetadata(key, Util.getClass(target), propertyKey!); + public static get(key: MetadataKeyType, target: any, propertyKey?: PropertyKeyType): any { + return Reflect.getMetadata(key, TypeUtil.getClass(target), propertyKey!); } /** @@ -80,8 +81,8 @@ export class Metadata { * ``` * */ - public static getOwn(key: string, target: any, propertyKey?: string | symbol): any { - return Reflect.getOwnMetadata(key, Util.getClass(target), propertyKey!); + public static getOwn(key: MetadataKeyType, target: any, propertyKey?: PropertyKeyType): any { + return Reflect.getOwnMetadata(key, TypeUtil.getClass(target), propertyKey!); } /** @@ -112,7 +113,7 @@ export class Metadata { * ``` * */ - public static getType(target: any, propertyKey?: string | symbol): any { + public static getType(target: any, propertyKey?: PropertyKeyType): any { return Reflect.getMetadata(DESIGN_TYPE, target, propertyKey!); } @@ -144,7 +145,7 @@ export class Metadata { * ``` * */ - public static getOwnType(target: any, propertyKey?: string | symbol): any { + public static getOwnType(target: any, propertyKey?: PropertyKeyType): any { return Reflect.getMetadata(DESIGN_TYPE, target, propertyKey!); } @@ -176,7 +177,7 @@ export class Metadata { * ``` * */ - public static getReturnType(target: any, propertyKey?: string | symbol): any { + public static getReturnType(target: any, propertyKey?: PropertyKeyType): any { return Reflect.getMetadata(DESIGN_RETURN_TYPE, target, propertyKey!); } @@ -208,7 +209,7 @@ export class Metadata { * ``` * */ - public static getOwnReturnType(target: any, propertyKey?: string | symbol): any { + public static getOwnReturnType(target: any, propertyKey?: PropertyKeyType): any { return Reflect.getOwnMetadata(DESIGN_RETURN_TYPE, target, propertyKey!); } @@ -247,8 +248,8 @@ export class Metadata { * ``` * */ - public static has(key: string, target: any, propertyKey?: string | symbol): boolean { - return Reflect.hasMetadata(key, Util.getClass(target), propertyKey!); + public static has(key: MetadataKeyType, target: any, propertyKey?: PropertyKeyType): boolean { + return Reflect.hasMetadata(key, TypeUtil.getClass(target), propertyKey!); } /** @@ -286,8 +287,8 @@ export class Metadata { * ``` * */ - public static hasOwn(key: string, target: any, propertyKey?: string | symbol): boolean { - return Reflect.hasOwnMetadata(key, Util.getClass(target), propertyKey!); + public static hasOwn(key: MetadataKeyType, target: any, propertyKey?: PropertyKeyType): boolean { + return Reflect.hasOwnMetadata(key, TypeUtil.getClass(target), propertyKey!); } /** @@ -325,8 +326,8 @@ export class Metadata { * ``` * */ - public static delete(key: string, target: any, propertyKey?: string | symbol): boolean { - return Reflect.deleteMetadata(key, Util.getClass(target), propertyKey!); + public static delete(key: MetadataKeyType, target: any, propertyKey?: PropertyKeyType): boolean { + return Reflect.deleteMetadata(key, TypeUtil.getClass(target), propertyKey!); } /** @@ -364,7 +365,7 @@ export class Metadata { * ``` * */ - public static setParamTypes(target: any, propertyKey: string | symbol, value: any): void { + public static setParamTypes(target: any, propertyKey: PropertyKeyType, value: any): void { return this.set(DESIGN_PARAM_TYPES, value, target.prototype, propertyKey); } @@ -372,7 +373,7 @@ export class Metadata { * Get all metadata for a metadataKey. * @param metadataKey */ - public static getTargetsFromPropertyKey = (metadataKey: string | symbol): any[] => + public static getTargetsFromPropertyKey = (metadataKey: MetadataKeyType): any[] => PROPERTIES.has(metadataKey) ? PROPERTIES.get(metadataKey) || [] : [] /** @@ -415,17 +416,17 @@ export class Metadata { * ``` * */ - public static set(key: string, value: any, target: any, propertyKey?: string | symbol): void { + public static set(key: MetadataKeyType, value: any, target: any, propertyKey?: PropertyKeyType): void { const targets: any[] = PROPERTIES.has(key) ? PROPERTIES.get(key) || [] : []; - const classConstructor = Util.getClass(target); + const classConstructor = TypeUtil.getClass(target); if (targets.indexOf(classConstructor) === -1) { targets.push(classConstructor); PROPERTIES.set(key, targets); } - Reflect.defineMetadata(key, value, Util.getClass(target), propertyKey!); + Reflect.defineMetadata(key, value, TypeUtil.getClass(target), propertyKey!); } /** @@ -456,7 +457,7 @@ export class Metadata { * ``` * */ - public static getParamTypes(target: any, propertyKey?: string | symbol): any[] { + public static getParamTypes(target: any, propertyKey?: PropertyKeyType): any[] { return Reflect.getMetadata(DESIGN_PARAM_TYPES, target, propertyKey!) || []; } @@ -488,7 +489,7 @@ export class Metadata { * ``` * */ - public static getOwnParamTypes(target: any, propertyKey?: string | symbol): any[] { + public static getOwnParamTypes(target: any, propertyKey?: PropertyKeyType): any[] { return Reflect.getOwnMetadata(DESIGN_PARAM_TYPES, target, propertyKey!) || []; } } @@ -517,4 +518,4 @@ const DESIGN_RETURN_TYPE = 'design:returntype'; * @private * @type {string} */ -const PROPERTIES: Map = new Map(); +const PROPERTIES: Map = new Map(); diff --git a/src/ts/@overflow/commons/core/type/type.ts b/src/ts/@overflow/commons/core/type/type.ts index 1dd8c5f..d26e3e0 100644 --- a/src/ts/@overflow/commons/core/type/type.ts +++ b/src/ts/@overflow/commons/core/type/type.ts @@ -1,8 +1,9 @@ export type IdentityType = T | symbol; export type PropertyKeyType = IdentityType; +export type MetadataKeyType = IdentityType; -export const Type = Function; -export interface Type extends Function { +export const ConstructorType = Function; +export interface ConstructorType extends Function { new (...args: any[]): T; } diff --git a/src/ts/@overflow/commons/core/type/util.ts b/src/ts/@overflow/commons/core/type/util.ts index c000388..4401412 100644 --- a/src/ts/@overflow/commons/core/type/util.ts +++ b/src/ts/@overflow/commons/core/type/util.ts @@ -1,390 +1,408 @@ import { + ConstructorType, DecoratorType, DecoratorParametersType, PrimitiveType, PropertyKeyType, } from './type'; -/** - * Get the provide constructor. - * @param targetClass - */ -export const getContructor = (targetClass: any): Function => - typeof targetClass === 'function' + +export class TypeUtil { + /** + * Get the provide constructor. + * @param targetClass + */ + public static getContructor(targetClass: any): ConstructorType { + return typeof targetClass === 'function' ? targetClass : targetClass.constructor; - -/** - * Get the provide constructor if target is an instance. - * @param target - * @returns {*} - */ -export function getClass(target: any): any { - return target.prototype ? target : target.constructor; -} - -/** - * - * @param target - * @returns {symbol} - */ -export function getClassOrSymbol(target: any): any { - return typeof target === 'symbol' ? target : getClass(target); -} - -/** - * Return true if the given obj is a primitive. - * @param target - * @returns {boolean} - */ -export function isPrimitiveOrPrimitiveClass(target: any): boolean { - return isString(target) - || isNumber(target) - || isBoolean(target); -} - -/** - * - * @param target - * @returns {PrimitiveType} - */ -export function primitiveOf(target: any): PrimitiveType { - if (isString(target)) { - return PrimitiveType.STRING; - } - if (isNumber(target)) { - return PrimitiveType.NUMBER; - } - if (isBoolean(target)) { - return PrimitiveType.BOOLEAN; - } - return PrimitiveType.ANY; -} - -/** - * - * @param target - * @returns {boolean} - */ -export function isString(target: any): boolean { - return typeof target === 'string' || target instanceof String || target === String; -} - -/** - * - * @param target - * @returns {boolean} - */ -export function isNumber(target: any): boolean { - return typeof target === 'number' || target instanceof Number || target === Number; -} - -/** - * - * @param target - * @returns {boolean} - */ -export function isBoolean(target: any): boolean { - return typeof target === 'boolean' || target instanceof Boolean || target === Boolean; -} - -/** - * - * @param target - * @returns {Boolean} - */ -export function isArray(target: any): boolean { - return Array.isArray(target); -} - -/** - * Return true if the clazz is an array. - * @param target - * @returns {boolean} - */ -export function isArrayOrArrayClass(target: any): boolean { - if (target === Array) { - return true; - } - return isArray(target); -} - -/** - * Return true if the target. - * @param target - * @returns {boolean} - */ -export function isCollection(target: any): boolean { - return isArrayOrArrayClass(target) - || target === Map - || target instanceof Map - || target === Set - || target instanceof Set - || target === WeakMap - || target instanceof WeakMap - || target === WeakSet - || target instanceof WeakSet; -} - -/** - * - * @param target - * @returns {boolean} - */ -export function isDate(target: any): boolean { - return target === Date || target instanceof Date; -} - -/** - * - * @param target - * @returns {boolean} - */ -export function isObject(target: any): boolean { - return target === Object; -} - -/** - * - * @param target - * @returns {boolean} - */ -export function isClass(target: any): boolean { - return !isPrimitiveOrPrimitiveClass(target) - && !isObject(target) - && !isDate(target) - && target !== undefined - && !isPromise(target); -} - -/** - * Return true if the value is an empty string, null or undefined. - * @param value - * @returns {boolean} - */ -export function isEmpty(value: any): boolean { - return value === '' || value === null || value === undefined; -} - -/** - * Get object name - */ -export const nameOf = (obj: any): string => { - switch (typeof obj) { - default: - return '' + obj; - case 'symbol': - return nameOfSymbol(obj); - case 'function': - return nameOfClass(obj); - } -}; - -/** - * Get the provide name. - * @param targetClass - */ -export const nameOfClass = (targetClass: any) => { - return typeof targetClass === 'function' - ? targetClass.name - : targetClass.constructor.name; -}; -/** - * Get symbol name. - * @param sym - */ -export const nameOfSymbol = (sym: symbol): string => - sym.toString().replace('Symbol(', '').replace(')', ''); - -/** - * - * @param out - * @param obj - * @param {{[p: string]: (collection: any[], value: any) => any}} reducers - * @returns {any} - */ -export function deepExtends(out: any, obj: any, reducers: { [key: string]: (collection: any[], value: any) => any } = {}): any { - - if (obj === undefined || obj === null) { - return obj; } - if (isPrimitiveOrPrimitiveClass(obj) || typeof obj === 'symbol' || typeof obj === 'function') { - return obj; + /** + * Get the provide constructor if target is an instance. + * @param target + * @returns {*} + */ + public static getClass(target: any): any { + return target.prototype ? target : target.constructor; } - if (isArrayOrArrayClass(obj)) { - out = out || []; - } else { - out = out || {}; + /** + * + * @param target + * @returns {symbol} + */ + public static getClassOrSymbol(target: any): any { + return typeof target === 'symbol' ? target : TypeUtil.getClass(target); } - const defaultReducer = reducers.default ? reducers.default : (collection: any[], value: any) => { - collection.push(value); - return collection; - }; - const set = (key: string | number, value: any) => { - if (isArrayOrArrayClass(obj)) { - out.push(value); - } else { - out[key] = value; - } - }; - - Object.keys(obj).forEach(key => { - let value = obj[key]; - - if (value === undefined || value === null) { - return; - } - - if (value === '' && out[key] !== '') { - return; - } - - if (isPrimitiveOrPrimitiveClass(value) || typeof value === 'function') { - set(key, value); - return; - } - - if (isArrayOrArrayClass(value)) { - - value = value.map((v: any) => deepExtends(undefined, v)); - - set(key, [] - .concat(out[key] || [], value) - .reduce((collection: any[], v: any) => - reducers[key] ? reducers[key](collection, v) : defaultReducer(collection, v), - [])); - return; - } - - // Object - if (isArrayOrArrayClass(obj)) { - set(key, deepExtends(undefined, value, reducers)); - } else { - set(key, deepExtends(out[key], value, reducers)); - } - }); - - if (isArrayOrArrayClass(out)) { - out.reduce((collection: any[], value: any) => defaultReducer(collection, value), []); + /** + * Return true if the given obj is a primitive. + * @param target + * @returns {boolean} + */ + public static isPrimitiveOrPrimitiveClass(target: any): boolean { + return TypeUtil.isString(target) + || TypeUtil.isNumber(target) + || TypeUtil.isBoolean(target); } - return out; -} - -/** - * - * @param target - * @returns {boolean} - */ -export function isPromise(target: any): boolean { - return target === Promise || target instanceof Promise; -} - -/** - * - * @param target - * @returns {any} - */ -export function getInheritedClass(target: any): any { - return Object.getPrototypeOf(target); -} - -/** - * - * @param {any[]} args - * @returns {DecoratorType} - */ -export function getDecoratorType(args: any[]): DecoratorType { - const [, propertyKey, descriptor] = args; - - if (typeof descriptor === 'number') { - return DecoratorType.PARAMETER; + /** + * + * @param target + * @returns {PrimitiveType} + */ + public static primitiveOf(target: any): PrimitiveType { + if (TypeUtil.isString(target)) { + return PrimitiveType.STRING; + } + if (TypeUtil.isNumber(target)) { + return PrimitiveType.NUMBER; + } + if (TypeUtil.isBoolean(target)) { + return PrimitiveType.BOOLEAN; + } + return PrimitiveType.ANY; } - if (propertyKey && descriptor === undefined || descriptor && (descriptor.get || descriptor.set)) { - return DecoratorType.PROPERTY; - } - return (descriptor && descriptor.value) ? DecoratorType.METHOD : DecoratorType.CLASS; -} - -/** - * - * @param target - * @param {PropertyKeyType} propertyKey - * @returns {PropertyDescriptor} - */ -export function descriptorOf(target: any, propertyKey: PropertyKeyType): PropertyDescriptor { - return Object.getOwnPropertyDescriptor(target && target.prototype || target, propertyKey)!; -} - -/** - * - * @param target - * @param {string} propertyKey - * @returns {DecoratorParametersType} - */ -export function decoratorArgs(target: any, propertyKey: string): DecoratorParametersType { - return [ - target, - propertyKey, - descriptorOf(target, propertyKey)!, - ]; -} - -/** - * - * @param target - * @returns {Array} - */ -export function ancestorsOf(target: any): any[] { - const classes = []; - - let currentTarget = getClass(target); - - while (nameOf(currentTarget) !== '') { - classes.unshift(currentTarget); - currentTarget = getInheritedClass(currentTarget); + /** + * + * @param target + * @returns {boolean} + */ + public static isString(target: any): boolean { + return typeof target === 'string' || target instanceof String || target === String; + } + + /** + * + * @param target + * @returns {boolean} + */ + public static isNumber(target: any): boolean { + return typeof target === 'number' || target instanceof Number || target === Number; + } + + /** + * + * @param target + * @returns {boolean} + */ + public static isBoolean(target: any): boolean { + return typeof target === 'boolean' || target instanceof Boolean || target === Boolean; + } + + /** + * + * @param target + * @returns {Boolean} + */ + public static isArray(target: any): boolean { + return Array.isArray(target); + } + + /** + * Return true if the clazz is an array. + * @param target + * @returns {boolean} + */ + public static isArrayOrArrayClass(target: any): boolean { + if (target === Array) { + return true; + } + return TypeUtil.isArray(target); + } + + /** + * Return true if the target. + * @param target + * @returns {boolean} + */ + public static isCollection(target: any): boolean { + return TypeUtil.isArrayOrArrayClass(target) + || target === Map + || target instanceof Map + || target === Set + || target instanceof Set + || target === WeakMap + || target instanceof WeakMap + || target === WeakSet + || target instanceof WeakSet; + } + + /** + * + * @param target + * @returns {boolean} + */ + public static isDate(target: any): boolean { + return target === Date || target instanceof Date; + } + + /** + * + * @param target + * @returns {boolean} + */ + public static isMethod(target: any, propertyKey: PropertyKeyType): boolean { + if (typeof(target[propertyKey]) === undefined) { + return false; + } + return typeof target[propertyKey] === 'function'; + } + + /** + * + * @param target + * @returns {boolean} + */ + public static isObject(target: any): boolean { + return target === Object; + } + + /** + * + * @param target + * @returns {boolean} + */ + public static isClass(target: any): boolean { + return !TypeUtil.isPrimitiveOrPrimitiveClass(target) + && !TypeUtil.isObject(target) + && !TypeUtil.isDate(target) + && target !== undefined + && !TypeUtil.isPromise(target); + } + + /** + * Return true if the value is an empty string, null or undefined. + * @param value + * @returns {boolean} + */ + public static isEmpty(value: any): boolean { + return value === '' || value === null || value === undefined; + } + + /** + * Get object name + */ + public static nameOf(obj: any): string { + switch (typeof obj) { + default: + return '' + obj; + case 'symbol': + return TypeUtil.nameOfSymbol(obj); + case 'function': + return TypeUtil.nameOfClass(obj); + } + } + + /** + * Get the provide name. + * @param targetClass + */ + public static nameOfClass(targetClass: any): string { + return typeof targetClass === 'function' + ? targetClass.name + : targetClass.constructor.name; + } + /** + * Get symbol name. + * @param sym + */ + public static nameOfSymbol(sym: symbol): string { + return sym.toString().replace('Symbol(', '').replace(')', ''); + } + /** + * + * @param out + * @param obj + * @param {{[p: string]: (collection: any[], value: any) => any}} reducers + * @returns {any} + */ + public static deepExtends(out: any, obj: any, reducers: { [key: string]: (collection: any[], value: any) => any } = {}): any { + + if (obj === undefined || obj === null) { + return obj; + } + + if (TypeUtil.isPrimitiveOrPrimitiveClass(obj) || typeof obj === 'symbol' || typeof obj === 'function') { + return obj; + } + + if (TypeUtil.isArrayOrArrayClass(obj)) { + out = out || []; + } else { + out = out || {}; + } + + const defaultReducer = reducers.default ? reducers.default : (collection: any[], value: any) => { + collection.push(value); + return collection; + }; + const set = (key: string | number, value: any) => { + if (TypeUtil.isArrayOrArrayClass(obj)) { + out.push(value); + } else { + out[key] = value; + } + }; + + Object.keys(obj).forEach(key => { + let value = obj[key]; + + if (value === undefined || value === null) { + return; + } + + if (value === '' && out[key] !== '') { + return; + } + + if (TypeUtil.isPrimitiveOrPrimitiveClass(value) || typeof value === 'function') { + set(key, value); + return; + } + + if (TypeUtil.isArrayOrArrayClass(value)) { + + value = value.map((v: any) => TypeUtil.deepExtends(undefined, v)); + + set(key, [] + .concat(out[key] || [], value) + .reduce((collection: any[], v: any) => + reducers[key] ? reducers[key](collection, v) : defaultReducer(collection, v), + [])); + return; + } + + // Object + if (TypeUtil.isArrayOrArrayClass(obj)) { + set(key, TypeUtil.deepExtends(undefined, value, reducers)); + } else { + set(key, TypeUtil.deepExtends(out[key], value, reducers)); + } + }); + + if (TypeUtil.isArrayOrArrayClass(out)) { + out.reduce((collection: any[], value: any) => defaultReducer(collection, value), []); + } + + return out; + } + + /** + * + * @param target + * @returns {boolean} + */ + public static isPromise(target: any): boolean { + return target === Promise || target instanceof Promise; + } + + /** + * + * @param target + * @returns {any} + */ + public static getInheritedClass(target: any): any { + return Object.getPrototypeOf(target); + } + + /** + * + * @param {any[]} args + * @returns {DecoratorType} + */ + public static getDecoratorType(args: any[]): DecoratorType { + const [, propertyKey, descriptor] = args; + + if (typeof descriptor === 'number') { + return DecoratorType.PARAMETER; + } + + if (propertyKey && descriptor === undefined || descriptor && (descriptor.get || descriptor.set)) { + return DecoratorType.PROPERTY; + } + return (descriptor && descriptor.value) ? DecoratorType.METHOD : DecoratorType.CLASS; + } + + /** + * + * @param target + * @param {PropertyKeyType} propertyKey + * @returns {PropertyDescriptor} + */ + public static descriptorOf(target: any, propertyKey: PropertyKeyType): PropertyDescriptor { + return Object.getOwnPropertyDescriptor(target && target.prototype || target, propertyKey)!; + } + + /** + * + * @param target + * @param {string} propertyKey + * @returns {DecoratorParametersType} + */ + public static decoratorArgs(target: any, propertyKey: string): DecoratorParametersType { + return [ + target, + propertyKey, + TypeUtil.descriptorOf(target, propertyKey)!, + ]; + } + + /** + * + * @param target + * @returns {Array} + */ + public static ancestorsOf(target: any): any[] { + const classes = []; + + let currentTarget = TypeUtil.getClass(target); + + while (TypeUtil.nameOf(currentTarget) !== '') { + classes.unshift(currentTarget); + currentTarget = TypeUtil.getInheritedClass(currentTarget); + } + + return classes; + } + + /** + * + * @param target + * @param {string} name + * @param {Function} callback + */ + public static applyBefore(target: any, name: string, callback: Function): void { + const original = target[name]; + target[name] = function (...args: any[]): any { + callback(...args); + return original.apply(this, args); + }; + } + + /** + * + * @param {Promise} promise + * @param {number} time + * @returns {Promise} + */ + public static promiseTimeout(promise: Promise, time: number = 1000): Promise<{ ok: boolean, response: any }> { + const timeout = (p: Promise, t: number) => new Promise((resolve) => { + p.then((response) => { + resolve(); + return response; + }); + setTimeout(() => resolve({ok: false}), t); + }); + + promise = promise.then((response) => ({ok: true, response})); + + return Promise.race([ + promise, + timeout(promise, time), + ]); } - return classes; -} - -/** - * - * @param target - * @param {string} name - * @param {Function} callback - */ -export function applyBefore(target: any, name: string, callback: Function): void { - const original = target[name]; - target[name] = function (...args: any[]): any { - callback(...args); - return original.apply(this, args); - }; -} - -/** - * - * @param {Promise} promise - * @param {number} time - * @returns {Promise} - */ -export function promiseTimeout(promise: Promise, time: number = 1000): Promise<{ ok: boolean, response: any }> { - const timeout = (p: Promise, t: number) => new Promise((resolve) => { - p.then((response) => { - resolve(); - return response; - }); - setTimeout(() => resolve({ok: false}), t); - }); - - promise = promise.then((response) => ({ok: true, response})); - - return Promise.race([ - promise, - timeout(promise, time), - ]); } diff --git a/src/ts/@overflow/commons/decorator/annotation/annotation.ts b/src/ts/@overflow/commons/decorator/annotation/annotation.ts index 2a7be78..956f9a1 100644 --- a/src/ts/@overflow/commons/decorator/annotation/annotation.ts +++ b/src/ts/@overflow/commons/decorator/annotation/annotation.ts @@ -1,8 +1,10 @@ import { + MetadataKeyType, PropertyKeyType, } from '@overflow/commons/core/type'; export interface Annotation { + metadataKey(): MetadataKeyType; classDecorator?(target: TFunction): TFunction | void; propertyDecorator?(target: Object, propertyKey: PropertyKeyType): void; methodDecorator?(target: Object, propertyKey: PropertyKeyType, diff --git a/src/ts/@overflow/commons/decorator/decorator.ts b/src/ts/@overflow/commons/decorator/decorator.ts index e062245..3e2ad7b 100644 --- a/src/ts/@overflow/commons/decorator/decorator.ts +++ b/src/ts/@overflow/commons/decorator/decorator.ts @@ -1,17 +1,18 @@ import { + ConstructorType, DecoratorType, - Type, + MetadataKeyType, + Metadata, + TypeUtil, } from '@overflow/commons/core/type'; -import * as TypeUtil from '@overflow/commons/core/type/util'; - import * as Constants from './constants'; import { Annotation, } from './annotation'; export class Decorator { - public static create = (AnnotationClass: Type) => { + public static create = (AnnotationClass: ConstructorType) => { return (...handlerArgs: any[]) => { let annotation: Annotation = new AnnotationClass(...handlerArgs); @@ -20,8 +21,15 @@ export class Decorator { return (...decoratorArgs: any[]) => { let decoratorType: DecoratorType = TypeUtil.getDecoratorType(decoratorArgs); + const [target, propertyKey, descriptorOrParameterIndex] = decoratorArgs; + // let type = typeof decoratorArgs[0] === 'function' ? decoratorArgs[0].prototype : decoratorArgs[0]; // let clazz = TypeUtil.getClass(decoratorArgs[0]); + if (typeof(annotation.metadataKey) === 'undefined') { + throw new Error(`Annotation[@${name}] must be implements metadataKey().`); + } + let metadataKey: MetadataKeyType = annotation.metadataKey.call(annotation); + Metadata.set(metadataKey, annotation, target, propertyKey); switch(decoratorType) { case DecoratorType.CLASS: @@ -29,26 +37,27 @@ export class Decorator { throw new Error(`Cannot apply @${name} decorator on Class.`); } - return annotation.classDecorator.call(annotation, decoratorArgs[0]); + return annotation.classDecorator.call(annotation, target); case DecoratorType.PROPERTY: if (typeof(annotation.propertyDecorator) === 'undefined') { throw new Error(`Cannot apply @${name} decorator on Property.`); } - return annotation.propertyDecorator.call(annotation, decoratorArgs[0], decoratorArgs[1]); + return annotation.propertyDecorator.call(annotation, target, propertyKey); case DecoratorType.METHOD: if (typeof(annotation.methodDecorator) === 'undefined') { throw new Error(`Cannot apply @${name} decorator on Method.`); } - return annotation.methodDecorator.call(annotation, decoratorArgs[0], decoratorArgs[1], decoratorArgs[2]); + return annotation.methodDecorator.call(annotation, target, propertyKey, descriptorOrParameterIndex); case DecoratorType.PARAMETER: if (typeof(annotation.parameterDecorator) === 'undefined') { throw new Error(`Cannot apply @${name} decorator on Parameter.`); } - return annotation.parameterDecorator.call(annotation, decoratorArgs[0], decoratorArgs[1], decoratorArgs[2]); + return annotation.parameterDecorator.call(annotation, target, propertyKey, descriptorOrParameterIndex); default: + throw new Error(`Cannot determine decorator[@${name}] type.`); } }; }; diff --git a/src/ts/@overflow/commons/redux/decorators/action_mapping.ts b/src/ts/@overflow/commons/redux/decorators/action_mapping.ts new file mode 100644 index 0000000..fb2ee7b --- /dev/null +++ b/src/ts/@overflow/commons/redux/decorators/action_mapping.ts @@ -0,0 +1,52 @@ +import { + MetadataKeyType, + PropertyKeyType, + TypeUtil, +} from '@overflow/commons/core/type'; + +import { + Annotation, + Decorator, +} from '@overflow/commons/decorator'; + +export const ActionMappingMetadataKey = Symbol('ActionMappingAnnotation'); + +export interface ActionMappingAnnotationAttributes { + value: string; +} + +export class ActionMappingAnnotation extends Annotation { + public readonly attributes: ActionMappingAnnotationAttributes; + + public constructor(value: string | ActionMappingAnnotationAttributes) { + super(); + + if (undefined === value) { + throw new Error(`value attribute must be specified.`); + } + + if (TypeUtil.isString(value)) { + this.attributes = { + value: value, + }; + } else { + this.attributes = value; + } + } + + public metadataKey(): MetadataKeyType { + return ActionMappingMetadataKey; + } + + public classDecorator(target: TFunction): TFunction | void { + console.log('ActionMapping'); + } + + public methodDecorator(target: Object, propertyKey: PropertyKeyType, + descriptor: TypedPropertyDescriptor): TypedPropertyDescriptor | void { + console.log('ActionMapping'); + } + +} + +export const ActionMapping = Decorator.create(ActionMappingAnnotation); diff --git a/src/ts/@overflow/commons/redux/decorators/index.ts b/src/ts/@overflow/commons/redux/decorators/index.ts index 895421b..e05e0ee 100644 --- a/src/ts/@overflow/commons/redux/decorators/index.ts +++ b/src/ts/@overflow/commons/redux/decorators/index.ts @@ -1,3 +1,4 @@ +export * from './action_mapping'; export * from './reducer'; export * from './rest_api'; export * from './rpc_api'; diff --git a/src/ts/@overflow/commons/redux/decorators/reducer.ts b/src/ts/@overflow/commons/redux/decorators/reducer.ts index 480513a..2a2fec6 100644 --- a/src/ts/@overflow/commons/redux/decorators/reducer.ts +++ b/src/ts/@overflow/commons/redux/decorators/reducer.ts @@ -1,13 +1,17 @@ -import * as TypeUtil from '@overflow/commons/core/type/util'; +import { + MetadataKeyType, + TypeUtil, +} from '@overflow/commons/core/type'; + import { Annotation, Decorator, } from '@overflow/commons/decorator'; - +export const ReducerMetadataKey = Symbol('ReducerAnnotation'); export interface ReducerAnnotationAttributes { - name: string; + name?: string; } export class ReducerAnnotation extends Annotation { @@ -15,6 +19,14 @@ export class ReducerAnnotation extends Annotation { public constructor(name: string | ReducerAnnotationAttributes) { super(); + + if (undefined === name) { + this.attributes = { + name: '', + }; + return; + } + if (TypeUtil.isString(name)) { this.attributes = { name: name, @@ -24,6 +36,10 @@ export class ReducerAnnotation extends Annotation { } } + public metadataKey(): MetadataKeyType { + return ReducerMetadataKey; + } + public classDecorator(target: TFunction): TFunction | void { console.log('Reducer'); } diff --git a/src/ts/@overflow/commons/redux/decorators/rest_api.ts b/src/ts/@overflow/commons/redux/decorators/rest_api.ts index 35fbbe2..5858e80 100644 --- a/src/ts/@overflow/commons/redux/decorators/rest_api.ts +++ b/src/ts/@overflow/commons/redux/decorators/rest_api.ts @@ -1,9 +1,15 @@ -import * as TypeUtil from '@overflow/commons/core/type/util'; +import { + MetadataKeyType, + PropertyKeyType, + TypeUtil, +} from '@overflow/commons/core/type'; + import { Annotation, Decorator, } from '@overflow/commons/decorator'; -import { PropertyKeyType } from '@overflow/commons/core/type'; + +export const RestAPIMetadataKey = Symbol('RestAPIAnnotation'); export interface RestAPIAnnotationAttributes { entryPath: string; @@ -14,6 +20,11 @@ export class RestAPIAnnotation extends Annotation { public constructor(entryPath: string | RestAPIAnnotationAttributes) { super(); + + if (undefined === entryPath) { + throw new Error(`entryPath attribute must be specified.`); + } + if (TypeUtil.isString(entryPath)) { this.attributes = { entryPath: entryPath, @@ -23,6 +34,10 @@ export class RestAPIAnnotation extends Annotation { } } + public metadataKey(): MetadataKeyType { + return RestAPIMetadataKey; + } + public methodDecorator(target: Object, propertyKey: PropertyKeyType, descriptor: TypedPropertyDescriptor): TypedPropertyDescriptor | void { console.log('RestAPI'); diff --git a/src/ts/@overflow/commons/redux/decorators/rpc_api.ts b/src/ts/@overflow/commons/redux/decorators/rpc_api.ts index 95d1b79..9d085a5 100644 --- a/src/ts/@overflow/commons/redux/decorators/rpc_api.ts +++ b/src/ts/@overflow/commons/redux/decorators/rpc_api.ts @@ -1,9 +1,15 @@ -import * as TypeUtil from '@overflow/commons/core/type/util'; +import { + MetadataKeyType, + PropertyKeyType, + TypeUtil, +} from '@overflow/commons/core/type'; + import { Annotation, Decorator, } from '@overflow/commons/decorator'; -import { PropertyKeyType } from '@overflow/commons/core/type'; + +export const RpcAPIMetadataKey = Symbol('RpcAPIAnnotation'); export interface RpcAPIAnnotationAttributes { method: string; @@ -14,6 +20,11 @@ export class RpcAPIAnnotation extends Annotation { public constructor(method: string | RpcAPIAnnotationAttributes) { super(); + + if (undefined === method) { + throw new Error(`method attribute must be specified.`); + } + if (TypeUtil.isString(method)) { this.attributes = { method: method, @@ -23,6 +34,10 @@ export class RpcAPIAnnotation extends Annotation { } } + public metadataKey(): MetadataKeyType { + return RpcAPIMetadataKey; + } + public methodDecorator(target: Object, propertyKey: PropertyKeyType, descriptor: TypedPropertyDescriptor): TypedPropertyDescriptor | void { console.log('RestAPI'); diff --git a/src/ts/@overflow/commons/redux/lpc_reducer.ts b/src/ts/@overflow/commons/redux/lpc_reducer.ts index 38630c5..98efea1 100644 --- a/src/ts/@overflow/commons/redux/lpc_reducer.ts +++ b/src/ts/@overflow/commons/redux/lpc_reducer.ts @@ -1,6 +1,17 @@ import { Action, } from './action'; +import { + ConstructorType, + Metadata, + TypeUtil, +} from '@overflow/commons/core/type'; +import { + ActionMappingAnnotation, + ActionMappingMetadataKey, + ReducerMetadataKey, + ReducerAnnotation, +} from '@overflow/commons/redux/decorators'; export default class LPCReducer { @@ -32,11 +43,38 @@ export default class LPCReducer { } /** - * registerReducer + * registerReducers */ - public registerReducer(reducerType: any): void { - console.log(``); + public registerReducers(reducerTypes: ConstructorType[]): void { + for (let reducerType of reducerTypes) { + let ra: ReducerAnnotation = Metadata.get(ReducerMetadataKey, reducerType); + if (undefined === ra) { + continue; + } + console.log(`name: ${ra.attributes.name}`); + let ama: ActionMappingAnnotation = Metadata.get(ActionMappingMetadataKey, reducerType); + if (undefined === ama) { + continue; + } + console.log(`actionMapping-value: ${ama.attributes.value}`); + + let properties = Object.keys(reducerType.prototype); + for (let propertyKey of properties) { + if (!TypeUtil.isMethod(reducerType.prototype, propertyKey)) { + console.log(`property: ${propertyKey}`); + continue; + } + console.log(`method: ${propertyKey}`); + } + } } + /** + * registerReducer + */ + public registerReducer(reducerType: ConstructorType): void { + console.log(``); + + } } diff --git a/src/ts/@overflow/modules/member/reducer/member_reducer.ts b/src/ts/@overflow/modules/member/reducer/member_reducer.ts index a3de460..2c5cef0 100644 --- a/src/ts/@overflow/modules/member/reducer/member_reducer.ts +++ b/src/ts/@overflow/modules/member/reducer/member_reducer.ts @@ -1,14 +1,18 @@ import { + ActionMapping, Reducer, RestAPI, } from '@overflow/commons/redux/decorators'; -@Reducer('@overflow/modules/member/MemberReducer') +@Reducer() +@ActionMapping('@overflow/modules/member/MemberReducer') export default class MemberReducer { + public path: string; /** * signin */ @RestAPI('/account/signin') + @ActionMapping('/signin') public signin(state: any, result: any, error: any): any { return state; diff --git a/src/ts/@overflow/webapp/config/index.ts b/src/ts/@overflow/webapp/config/index.ts index ac40ef1..d925cbd 100644 --- a/src/ts/@overflow/webapp/config/index.ts +++ b/src/ts/@overflow/webapp/config/index.ts @@ -1,7 +1,8 @@ +import { ConstructorType } from '@overflow/commons/core/type'; import MemberReducer from '@overflow/modules/member/reducer/member_reducer'; -const reducers: any[] = [ +const reducers: ConstructorType[] = [ MemberReducer, ]; @@ -11,7 +12,7 @@ const state: any = { export interface ReduxConfig { state: any; - reducers: any[]; + reducers: ConstructorType[]; } const reduxConfig: ReduxConfig = { diff --git a/src/ts/@overflow/webapp/index.tsx b/src/ts/@overflow/webapp/index.tsx index d3cb3c7..f128344 100644 --- a/src/ts/@overflow/webapp/index.tsx +++ b/src/ts/@overflow/webapp/index.tsx @@ -58,6 +58,7 @@ class WebAppApplication { try { this.renderLoading(); let reducer: WebAppReducer = WebAppReducer.getInstance(); + reducer.registerReducers(config.redux.reducers); this.store = createStore(reducer.reducer, config.redux.state); this.renderApp(); diff --git a/src/ts/@overflow/webapp/pages/webapp.tsx b/src/ts/@overflow/webapp/pages/webapp.tsx index b43b9bc..78d2fbf 100644 --- a/src/ts/@overflow/webapp/pages/webapp.tsx +++ b/src/ts/@overflow/webapp/pages/webapp.tsx @@ -14,7 +14,7 @@ class WebApp extends React.Component { public render(): JSX.Element { return ( - Hello... + Hello. ); } }