This commit is contained in:
crusader 2018-09-05 23:47:01 +09:00
parent 036d91898d
commit e81ce83303
10 changed files with 247 additions and 2 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "@overflow/core-js", "name": "@overflow/core-js",
"version": "0.0.4", "version": "0.0.7",
"description": "TypeScript library setup for multiple compilation targets using tsc and webpack", "description": "TypeScript library setup for multiple compilation targets using tsc and webpack",
"main": "./bundles/index.umd.js", "main": "./bundles/index.umd.js",
"module": "./esm5/index.js", "module": "./esm5/index.js",

View File

@ -4,3 +4,17 @@ export class IllegalArgumentError extends Error {
Object.setPrototypeOf(this, new.target.prototype); Object.setPrototypeOf(this, new.target.prototype);
} }
} }
export class NotSupportedDecoratorError extends Error {
public constructor(message?: string) {
super(message);
Object.setPrototypeOf(this, new.target.prototype);
}
}
export class NotDecoratedClassError extends Error {
public constructor(message?: string) {
super(message);
Object.setPrototypeOf(this, new.target.prototype);
}
}

View File

@ -14,3 +14,12 @@ export enum PrimitiveType {
NUMBER = 'number', NUMBER = 'number',
BOOLEAN = 'boolean', BOOLEAN = 'boolean',
} }
export enum DecoratorType {
CLASS = 'Clazz',
PROPERTY = 'Property',
METHOD = 'Method',
PARAMETER = 'Parameter',
}
export type DecoratorParametersType = [any, string | symbol, number | PropertyDescriptor | undefined];

View File

@ -0,0 +1,23 @@
import {
PropertyKeyType,
} from '../core';
import {
Annotation,
} from '../reflect';
export interface Decorator<Attribute = {}> {
classDecorator?: <TFunction extends Function>(target: TFunction) => TFunction | void;
propertyDecorator?: (target: Object, propertyKey: PropertyKeyType) => void;
methodDecorator?: <T>(target: Object, propertyKey: PropertyKeyType,
descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void;
parameterDecorator?: (target: Object, propertyKey: PropertyKeyType, parameterIndex: number) => void;
}
export abstract class Decorator<Attribute = {}> extends Annotation {
public constructor(attribute?: Attribute) {
super(attribute);
}
}

View File

@ -0,0 +1,163 @@
import {
DecoratorParametersType,
DecoratorType,
NotSupportedDecoratorError,
PropertyKeyType, Type,
} from '../core';
import {
Annotation,
Class,
Constructor,
Field,
Metadata,
Method,
Parameter,
} from '../reflect';
import {
TypeUtil,
} from '../util';
import { Decorator } from './Decorator';
export class DecoratorHelper {
public static register<AttributeType>(attribute: AttributeType, ...decoratorArgs: any[]) {
const annotation = new Annotation<AttributeType>(attribute);
const name: string = Annotation.name;
DecoratorHelper.registerAnnotation(name, annotation, decoratorArgs);
}
public static create = <Attribute = {}>(DecoratorClass: Type<Decorator<Attribute>>) => {
return (attribute: Attribute) => {
const annotation: Decorator<Attribute> = new DecoratorClass(attribute);
const name: string = DecoratorClass.name;
return (...decoratorArgs: any[]) => {
return DecoratorHelper.registerAnnotation(name, annotation, decoratorArgs);
};
};
}
private static registerAnnotation(name: string, annotation: any, decoratorArgs: any[]) {
const decoratorType: DecoratorType = DecoratorHelper.getDecoratorType(decoratorArgs);
const [target, propertyKey, descriptorOrParameterIndex] = decoratorArgs;
const clazz: Class = Class._defineClass(TypeUtil.getType(target));
let field: Field;
let method: Method;
let parameter: Parameter | undefined;
let cons: Constructor | null = null;
switch (decoratorType) {
case DecoratorType.CLASS:
try {
cons = clazz._defineConstructor(Metadata.getOwnParamTypes(target));
clazz._addAnnotation(annotation);
if (annotation instanceof Decorator && undefined !== annotation.classDecorator) {
return annotation.classDecorator.call(annotation, target);
}
} catch (e) {
if (e instanceof NotSupportedDecoratorError) {
throw new NotSupportedDecoratorError(`Cannot apply @${name} decorator on Class.`);
}
throw e;
}
break;
case DecoratorType.PROPERTY:
try {
field = clazz._defineField(propertyKey, Metadata.getOwnType(target, propertyKey));
field._addAnnotation(annotation);
if (annotation instanceof Decorator && undefined !== annotation.propertyDecorator) {
return annotation.propertyDecorator.call(annotation, target, propertyKey);
}
} catch (e) {
if (e instanceof NotSupportedDecoratorError) {
throw new NotSupportedDecoratorError(`Cannot apply @${name} decorator on Property.`);
}
throw e;
}
break;
case DecoratorType.METHOD:
try {
method = clazz._defineMethod(propertyKey,
Metadata.getOwnParamTypes(target, propertyKey),
Metadata.getOwnReturnType(target, propertyKey));
method._addAnnotation(annotation);
if (annotation instanceof Decorator && undefined !== annotation.methodDecorator) {
return annotation.methodDecorator.call(annotation, target, propertyKey, descriptorOrParameterIndex);
}
} catch (e) {
if (e instanceof NotSupportedDecoratorError) {
throw new NotSupportedDecoratorError(`Cannot apply @${name} decorator on Method.`);
}
throw e;
}
break;
case DecoratorType.PARAMETER:
try {
if (undefined === propertyKey) {
cons = clazz.getConstructor();
parameter = cons.getParameter(descriptorOrParameterIndex);
} else {
method = clazz._defineMethod(propertyKey,
Metadata.getOwnParamTypes(target, propertyKey),
Metadata.getOwnReturnType(target, propertyKey));
parameter = method.getParameter(descriptorOrParameterIndex);
}
if (undefined !== parameter) {
parameter._addAnnotation(annotation);
}
if (annotation instanceof Decorator && undefined !== annotation.parameterDecorator) {
return annotation.parameterDecorator.call(annotation, target, propertyKey, descriptorOrParameterIndex);
}
} catch (e) {
if (e instanceof NotSupportedDecoratorError) {
throw new NotSupportedDecoratorError(`Cannot apply @${name} decorator on Parameter.`);
}
throw e;
}
break;
default:
throw new NotSupportedDecoratorError(`Cannot determine decorator[@${name}] type.`);
}
}
/**
*
* @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 {string} propertyKey
* @returns {DecoratorParametersType}
*/
public static decoratorArgs(target: any, propertyKey: PropertyKeyType): DecoratorParametersType {
return [
target,
propertyKey,
TypeUtil.descriptorOf(target, propertyKey),
];
}
}

2
src/decorator/index.ts Normal file
View File

@ -0,0 +1,2 @@
export * from './Decorator';
export * from './DecoratorHelper';

View File

@ -1,3 +1,4 @@
export * from './core'; export * from './core';
export * from './decorator';
export * from './reflect'; export * from './reflect';
export * from './util'; export * from './util';

View File

@ -1,4 +1,4 @@
export abstract class Annotation<Attribute = {}> { export class Annotation<Attribute = {}> {
public readonly attribute: Attribute | undefined; public readonly attribute: Attribute | undefined;
public constructor(attribute?: Attribute) { public constructor(attribute?: Attribute) {

View File

@ -0,0 +1,32 @@
import { Method } from '../reflect';
export abstract class ReflectionUtil {
public static getParamTypes(method: Method): any[] {
if (0 === method.getParameterCount()) {
return [];
}
const parameters = method.getParameters();
if (undefined === parameters) {
return [];
}
const results: any[] = [];
for (let indexI = 0; indexI < parameters.length; indexI++) {
const paramType = parameters[indexI].getType();
results.push(paramType);
}
return results;
}
public static getParamTypeStrings(method: Method): string[] {
const paramTypes = ReflectionUtil.getParamTypes(method);
const results: string[] = [];
paramTypes.forEach(paramType => {
results.push(paramType.name);
});
return results;
}
}

View File

@ -1,3 +1,4 @@
export * from './AnnotationUtil'; export * from './AnnotationUtil';
export * from './ReflectionUtil';
export * from './Registry'; export * from './Registry';
export * from './TypeUtil'; export * from './TypeUtil';