@litert/decorator
Version:
The utility set of decorators.
454 lines • 20 kB
JavaScript
"use strict";
/* eslint-disable @typescript-eslint/no-confusing-void-expression */
/**
* Copyright 2023 Angus.Fenying <fenying@litert.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.DecoratorUtility = void 0;
class DecoratorUtility {
constructor() {
this._staticProperties = new Map();
this._staticMethods = new Map();
this._memberProperties = new Map();
this._memberMethods = new Map();
this._hookNativeReflectMetadata = false;
}
createGeneralDecorator(processors) {
return (c, k, d) => {
if (this.isClassConstructor(c)) {
if (k === undefined) {
switch (typeof d) {
case 'undefined': // for class
if (!processors.class) {
throw new TypeError('This decorator can not apply with classes.');
}
return processors.class(c);
case 'number': // for class constructor parameter
if (!processors.ctorParameter) {
throw new TypeError('This decorator can not apply with constructor parameters.');
}
return processors.ctorParameter(c, d);
default:
throw new TypeError('Invalid parameters for decorators.');
}
}
else {
switch (typeof k) {
case 'symbol':
case 'string':
break;
default:
throw new TypeError('Invalid parameters for decorators.');
}
switch (typeof d) {
case 'object': // for static method
if (d.set || d.get) {
if (!processors.staticAccessor) {
throw new TypeError('This decorator can not apply with static accessors.');
}
this._registerStaticProperty(c, k);
return processors.staticAccessor(c, k, d);
}
else {
if (!processors.staticMethod) {
throw new TypeError('This decorator can not apply with static methods.');
}
this._registerStaticMethod(c, k);
return processors.staticMethod(c, k, d);
}
case 'number': // for static method parameter
if (processors.staticMethodParameter) {
this._registerStaticMethod(c, k);
return processors.staticMethodParameter(c, k, d);
}
throw new TypeError('This decorator can not apply with static method parameters.');
case 'undefined': // for static property
if (!processors.staticProperty) {
throw new TypeError('This decorator can not apply with static properties.');
}
this._registerStaticProperty(c, k);
return processors.staticProperty(c, k);
default:
throw new TypeError('Invalid parameters for decorators.');
}
}
}
else if (this.isClassPrototype(c)) {
switch (typeof k) {
case 'symbol':
case 'string':
break;
default:
throw new TypeError('Invalid parameters for decorators.');
}
switch (typeof d) {
case 'object': // for member method
if (d.set || d.get) {
if (!processors.accessor) {
throw new TypeError('This decorator can not apply with member accessors.');
}
this._registerMemberProperty(c, k);
return processors.accessor(c, k, d);
}
else {
if (!processors.method) {
throw new TypeError('This decorator can not apply with member methods.');
}
this._registerMemberMethod(c, k);
return processors.method(c, k, d);
}
case 'number': // for member member parameter
if (!processors.methodParameter) {
throw new TypeError('This decorator can not apply with member method parameters.');
}
this._registerMemberMethod(c, k);
return processors.methodParameter(c, k, d);
case 'undefined': // for member property
if (!processors.property) {
throw new TypeError('This decorator can not apply with member properties.');
}
this._registerMemberProperty(c, k);
return processors.property(c, k);
default:
throw new TypeError('Invalid parameters for decorators.');
}
}
else {
throw new TypeError('Invalid parameters for decorators.');
}
};
}
isClassConstructor(target) {
return typeof target === 'function' && target.toString().startsWith('class');
}
isClassPrototype(target) {
return typeof target === 'object'
&& this.isClassConstructor(target === null || target === void 0 ? void 0 : target.constructor);
}
isInsideClassDecorator(args) {
return this.isClassConstructor(args[0]) && args[1] === undefined && args[2] === undefined;
}
isInsideConstructorParameterDecorator(args) {
return this.isClassConstructor(args[0])
&& args[1] === undefined
&& typeof args[2] === 'number';
}
isInsideMethodParameterDecorator(args) {
return this.isClassPrototype(args[0])
&& (typeof args[1] === 'symbol' || typeof args[1] === 'string')
&& typeof args[2] === 'number';
}
isInsideMethodDecorator(args) {
return this.isClassPrototype(args[0])
&& (typeof args[1] === 'symbol' || typeof args[1] === 'string')
&& typeof args[2] === 'object'
&& typeof args[2].value === 'function';
}
isInsideAccessorDecorator(args) {
return this.isClassPrototype(args[0])
&& (typeof args[1] === 'symbol' || typeof args[1] === 'string')
&& typeof args[2] === 'object'
&& (args[2].set !== undefined || args[2].get !== undefined);
}
isInsidePropertyDecorator(args) {
return this.isClassPrototype(args[0])
&& (typeof args[1] === 'symbol' || typeof args[1] === 'string')
&& args[2] === undefined;
}
isInsideStaticMethodParameterDecorator(args) {
return this.isClassConstructor(args[0])
&& (typeof args[1] === 'symbol' || typeof args[1] === 'string')
&& typeof args[2] === 'number'
&& this.isStaticMethod(args[0], args[1]);
}
isInsideStaticMethodDecorator(args) {
return this.isClassConstructor(args[0])
&& (typeof args[1] === 'symbol' || typeof args[1] === 'string')
&& typeof args[2] === 'object'
&& typeof args[2].value === 'function';
}
isInsideStaticAccessorDecorator(args) {
return this.isClassConstructor(args[0])
&& (typeof args[1] === 'symbol' || typeof args[1] === 'string')
&& typeof args[2] === 'object'
&& (args[2].set !== undefined || args[2].get !== undefined);
}
isInsideStaticPropertyDecorator(args) {
return this.isClassConstructor(args[0])
&& (typeof args[1] === 'symbol' || typeof args[1] === 'string')
&& args[2] === undefined;
}
createClassDecorator(decorator) {
return (ctor, k, d) => {
if (!this.isInsideClassDecorator([ctor, k, d])) {
throw new TypeError('Invalid parameters for class decorator.');
}
return decorator(ctor);
};
}
createConstructorParameterDecorator(decorator) {
return (ctor, method, index) => {
if (!this.isInsideConstructorParameterDecorator([ctor, method, index])) {
throw new TypeError('Invalid parameters for class constructor parameter decorator.');
}
return decorator(ctor, index);
};
}
createMethodParameterDecorator(decorator) {
return (proto, method, index) => {
if (!this.isInsideMethodParameterDecorator([proto, method, index])) {
throw new TypeError('Invalid parameters for class method parameter decorator.');
}
this._registerMemberMethod(proto, method);
return decorator(proto, method, index);
};
}
createMethodDecorator(decorator) {
return (proto, method, descriptor) => {
if (!this.isInsideMethodDecorator([proto, method, descriptor])) {
throw new TypeError('Invalid parameters for class method decorator.');
}
this._registerMemberMethod(proto, method);
return decorator(proto, method, descriptor);
};
}
createAccessorDecorator(decorator) {
return (proto, accessor, descriptor) => {
if (!this.isInsideAccessorDecorator([proto, accessor, descriptor])) {
throw new TypeError('Invalid parameters for class accessor decorator.');
}
this._registerMemberProperty(proto, accessor);
return decorator(proto, accessor, descriptor);
};
}
createPropertyDecorator(decorator) {
return (proto, name, descriptor) => {
if (!this.isInsidePropertyDecorator([proto, name, descriptor])) {
throw new TypeError('Invalid parameters for class property decorator.');
}
this._registerMemberProperty(proto, name);
return decorator(proto, name);
};
}
createStaticMethodParameterDecorator(decorator) {
return (ctor, method, index) => {
if (!this.isInsideStaticMethodParameterDecorator([ctor, method, index])) {
throw new TypeError('Invalid parameters for class static method parameter decorator.');
}
this._registerStaticMethod(ctor, method);
return decorator(ctor, method, index);
};
}
createStaticMethodDecorator(decorator) {
return (ctor, method, descriptor) => {
if (!this.isInsideStaticMethodDecorator([ctor, method, descriptor])) {
throw new TypeError('Invalid parameters for class static method decorator.');
}
this._registerStaticMethod(ctor, method);
return decorator(ctor, method, descriptor);
};
}
createStaticAccessorDecorator(decorator) {
return (ctor, accessor, descriptor) => {
if (!this.isInsideStaticAccessorDecorator([ctor, accessor, descriptor])) {
throw new TypeError('Invalid parameters for class static accessor decorator.');
}
this._registerStaticProperty(ctor, accessor);
return decorator(ctor, accessor, descriptor);
};
}
createStaticPropertyDecorator(decorator) {
return (ctor, name, descriptor) => {
if (!this.isInsideStaticPropertyDecorator([ctor, name, descriptor])) {
throw new TypeError('Invalid parameters for class static property decorator.');
}
this._registerStaticProperty(ctor, name);
return decorator(ctor, name);
};
}
getClassOfObject(obj) {
if (typeof obj !== 'object' || obj === null) {
throw new TypeError('Can not find the class of non-object or null.');
}
return obj.constructor;
}
getParentClass(cls) {
if (this.hasParentClass(cls)) {
return cls.__proto__;
}
return null;
}
hasParentClass(cls) {
return this.isClassConstructor(cls) && !!cls.__proto__.prototype;
}
_registerMemberProperty(proto, name) {
var _a;
const cls = (_a = this._memberProperties.get(proto.constructor)) !== null && _a !== void 0 ? _a : [];
if (!cls.includes(name)) {
cls.push(name);
}
this._memberProperties.set(proto.constructor, cls);
}
_registerMemberMethod(proto, name) {
var _a;
const cls = (_a = this._memberMethods.get(proto.constructor)) !== null && _a !== void 0 ? _a : [];
if (!cls.includes(name)) {
cls.push(name);
}
this._memberMethods.set(proto.constructor, cls);
}
_registerStaticProperty(ctor, name) {
var _a;
const cls = (_a = this._staticProperties.get(ctor)) !== null && _a !== void 0 ? _a : [];
if (!cls.includes(name)) {
cls.push(name);
}
this._staticProperties.set(ctor, cls);
}
_registerStaticMethod(ctor, name) {
var _a;
const cls = (_a = this._staticMethods.get(ctor)) !== null && _a !== void 0 ? _a : [];
if (!cls.includes(name)) {
cls.push(name);
}
this._staticMethods.set(ctor, cls);
}
isAccessor(ctor, name) {
const dtr = Object.getOwnPropertyDescriptor(ctor.prototype, name);
return (dtr === null || dtr === void 0 ? void 0 : dtr.get) !== undefined || (dtr === null || dtr === void 0 ? void 0 : dtr.set) !== undefined;
}
isStaticAccessor(ctor, name) {
const dtr = Object.getOwnPropertyDescriptor(ctor, name);
return (dtr === null || dtr === void 0 ? void 0 : dtr.get) !== undefined || (dtr === null || dtr === void 0 ? void 0 : dtr.set) !== undefined;
}
isMethod(ctor, name) {
const dtr = Object.getOwnPropertyDescriptor(ctor.prototype, name);
return typeof (dtr === null || dtr === void 0 ? void 0 : dtr.value) === 'function';
}
isStaticMethod(ctor, name) {
const dtr = Object.getOwnPropertyDescriptor(ctor, name);
return typeof (dtr === null || dtr === void 0 ? void 0 : dtr.value) === 'function';
}
getOwnMethodNames(target) {
var _a;
return (_a = this._memberMethods.get(target)) !== null && _a !== void 0 ? _a : [];
}
getOwnStaticMethodNames(target) {
var _a;
return (_a = this._staticMethods.get(target)) !== null && _a !== void 0 ? _a : [];
}
getOwnPropertyNames(target) {
var _a;
return (_a = this._memberProperties.get(target)) !== null && _a !== void 0 ? _a : [];
}
getOwnStaticPropertyNames(target) {
var _a;
return (_a = this._staticProperties.get(target)) !== null && _a !== void 0 ? _a : [];
}
isHookNativeReflectMetadata() {
return this._hookNativeReflectMetadata;
}
hookNativeReflectMetadata(enabled = true) {
if (this._hookNativeReflectMetadata === enabled) {
return;
}
// eslint-disable-next-line @typescript-eslint/no-require-imports
require('reflect-metadata');
const ref = Reflect;
if (!enabled) {
ref.metadata = this._bakOfMetadata;
ref.defineMetadata = this._bakOfDefineMetadata;
this._hookNativeReflectMetadata = false;
return;
}
this._bakOfMetadata = ref.metadata;
ref.metadata = (metadataKey, metadataValue) => {
return (target, propertyKey, dtr) => {
if (propertyKey !== undefined && typeof dtr !== 'number') {
if (typeof dtr === 'object') {
if (this.isClassConstructor(target)) {
this._registerStaticMethod(target, propertyKey);
}
else {
this._registerMemberMethod(target, propertyKey);
}
}
else {
if (this.isClassConstructor(target)) {
this._registerStaticProperty(target, propertyKey);
}
else {
this._registerMemberProperty(target, propertyKey);
}
}
}
if (dtr !== undefined) {
this._bakOfMetadata(metadataKey, metadataValue)(target, propertyKey, dtr);
}
else {
this._bakOfMetadata(metadataKey, metadataValue)(target, propertyKey);
}
};
};
this._bakOfDefineMetadata = ref.defineMetadata;
ref.defineMetadata = (metadataKey, metadataValue, target, propertyKey) => {
if (propertyKey !== undefined) {
if (typeof target[propertyKey] === 'function') {
if (this.isClassConstructor(target)) {
this._registerStaticMethod(target, propertyKey);
}
else {
this._registerMemberMethod(target, propertyKey);
}
}
else {
if (this.isClassConstructor(target)) {
this._registerStaticProperty(target, propertyKey);
}
else {
this._registerMemberProperty(target, propertyKey);
}
}
return this._bakOfDefineMetadata(metadataKey, metadataValue, target, propertyKey);
}
return this._bakOfDefineMetadata(metadataKey, metadataValue, target);
};
this._hookNativeReflectMetadata = true;
}
composite(decorators) {
return function (a, b, c) {
var _a, _b;
for (const d of decorators) {
if (typeof c === 'object') {
c = (_a = d(a, b, c)) !== null && _a !== void 0 ? _a : c;
}
else if (b === undefined && c === undefined) {
a = (_b = d(a, b, c)) !== null && _b !== void 0 ? _b : a;
}
else {
d(a, b, c);
}
}
if (typeof c === 'object') {
return c;
}
else if (b === undefined && c === undefined) {
return a;
}
};
}
}
exports.DecoratorUtility = DecoratorUtility;
//# sourceMappingURL=DecoratorUtility.js.map