UNPKG

typescript-class-helpers

Version:
625 lines (610 loc) 22.2 kB
import { _, Helpers } from 'tnp-core/browser'; export * from 'enum-values'; var Models; (function (Models) { /** * @deprecated */ class ParamConfig { } Models.ParamConfig = ParamConfig; /** * @deprecated */ class MethodConfig { constructor() { /* */ /* */ this.parameters = {}; } } Models.MethodConfig = MethodConfig; /** * @deprecated */ class ClassConfig { constructor() { // @ts-ignore this.singleton = void 0; this.injections = []; this.vChildren = []; this.methods = {}; } } Models.ClassConfig = ClassConfig; })(Models || (Models = {})); const SYMBOL = { MODELS_MAPPING: Symbol(), DEFAULT_MODEL: Symbol(), STORAGE: 'classesstorage', CLASSES: 'classes', ClassNameStaticProperty: '$$className$$' }; function defaultValues() { return _.cloneDeep({ [SYMBOL.CLASSES]: [] }); } function getStorage(property) { /* */ /* */ /* */ /* */ if (typeof property === 'string') { const storageInClassStaticProp = getStorage(); return storageInClassStaticProp[property]; } if (typeof defaultValues.prototype[SYMBOL.STORAGE] === 'undefined') { defaultValues.prototype[SYMBOL.STORAGE] = defaultValues(); } return defaultValues.prototype[SYMBOL.STORAGE]; } function getClasses$1() { const s = getStorage(); return s[SYMBOL.CLASSES]; } // @ts-ignore function setClassName(target, className, options) { let { classFamily, uniqueKey, classNameInBrowser, singleton } = options || { classFamily: void 0, uniqueKey: 'id', classNameInBrowser: void 0, singleton: void 0, autoinstance: false }; if (!_.isUndefined(singleton) && _.isBoolean(singleton) && singleton) { singleton = 'first-instance'; } if (!uniqueKey) { uniqueKey = 'id'; } if (target) { const config = _.first(CLASSNAME.getClassConfig(target)); config.className = className; config.uniqueKey = uniqueKey; config.classNameInBrowser = classNameInBrowser; // console.log(`Setting class Name to "${target.name}"`) } const existed = getClasses$1() .find(f => f.className === className); if (existed) { existed.target = target; } else { const res = { className, classNameInBrowser, target, uniqueKey, classFamily }; if (_.isUndefined(classFamily)) { Object.defineProperty(res, 'classFamily', { get: function () { const parent = Object.getPrototypeOf(target); if (!_.isFunction(parent) || parent.name === 'Object' || parent.name === '') { return className; } const classNameNew = CLASSNAME.getClassName(parent); return CLASSNAME.getClassFamilyByClassName(classNameNew); } }); } getClasses$1().push(res); } const Original = target; if (singleton === 'first-instance' || singleton === 'last-instance') { const obj = { // @ts-ignore decoratedConstructor: function (...args) { // console.log(`DECORATED CONSTRUCTOR OF ${Original.name}`) const context = Original.apply(this, args); const existedSingleton = CLASS.getSingleton(Original); if (!existedSingleton || singleton === 'last-instance') { CLASS.setSingletonObj(Original, this); CLASS.setSingletonObj(obj.decoratedConstructor, this); // console.log(`Singleton created for "${className}", mode: ${singleton} `); } else { // console.log('ingleton exists') } return context; } }; // copy prototype so intanceof operator still works obj.decoratedConstructor.prototype = Original.prototype; // @ts-ignore Object.keys(Original).forEach((name) => { obj.decoratedConstructor[name] = Original[name]; }); Object.defineProperty(obj.decoratedConstructor, 'name', { value: className, configurable: true, }); // (obj.decoratedConstructor as any).name = className; // console.log('return new contruor', decoratedConstructor) return obj.decoratedConstructor; } else if (singleton === 'autoinstance') { // console.log(`AUTOINSTANCE FOR ${target.name}`) const auto = new Original(); CLASS.setSingletonObj(Original, auto); // console.log(`Singleton created for "${className}", mode: ${singleton} `) } } const registerd = { configs: [], classes: [], }; const ERROR_MSG_CLASS_WITHOUT_DECORATOR = `[typescript-class-helpers][getClassName(...)](PRODUCTION MODE ERROR) Please use decoartor @CLASSNAME for each entity or controller This is preventing class name problem in minified code. import { CLASS } from 'typescript-class-helpers'; @CLASS.NAME('ExampleClass') class ExampleClass { ... } `; /* */ /* */ function getClasses() { const s = getStorage(); return s[SYMBOL.CLASSES]; } var CLASSNAME; (function (CLASSNAME_1) { function getClassConfig(target, configs = [], callerTarget) { if (!_.isFunction(target)) { throw `[typescript-class-helper][getClassConfig] Cannot get class config from: ${target}`; } let config; const parentClass = Object.getPrototypeOf(target); const isValidParent = _.isFunction(parentClass) && parentClass.name !== ''; if (registerd.classes.includes(target)) { config = registerd.configs[registerd.classes.findIndex(c => c === target)]; } else { config = new Models.ClassConfig(); config.classReference = target; registerd.classes.push(target); } registerd.configs[registerd.classes.findIndex(c => c === target)] = config; if (callerTarget) { const callerTargetConfig = registerd.configs[registerd.classes.findIndex(c => c === callerTarget)]; if (!config.vChildren.includes(callerTargetConfig)) { config.vChildren.push(callerTargetConfig); } callerTargetConfig.vParent = config; } configs.push(config); return isValidParent ? getClassConfig(parentClass, configs, target) : configs; } CLASSNAME_1.getClassConfig = getClassConfig; /** * PLEASE PROVIDE NAME AS TYPED STRING, NOT VARIABLE * Decorator requred for production mode * @param name Name of class */ function CLASSNAME(className, options) { return function (target) { // console.log(`CLASSNAME Inited ${className}`) return setClassName(target, className, options); }; } CLASSNAME_1.CLASSNAME = CLASSNAME; function getClassName(target, production = false) { if (_.isNil(target)) { // console.log(target); // Helpers.warn(`[typescript-class-helpers][getClassName] target is nil`) return void 0; } if (_.isString(target)) { // console.log(target); Helpers.log(`[typescript-class-helpers][getClassName] target is string: '${target}', produciton: ${production}`); return target; } if (target === Date) { return 'Date'; } if (target === FormData) { return 'FormData'; } if (!_.isFunction(target)) { // console.log(target); Helpers.log(`[typescript-class-helpers][getClassName] target is not a class`); return void 0; } if (target[SYMBOL.ClassNameStaticProperty]) { return target[SYMBOL.ClassNameStaticProperty]; } const configs = getClassConfig(target); const config = _.first(configs); const classNameInBrowser = config?.classNameInBrowser; if (Helpers.isBrowser && _.isString(classNameInBrowser)) { return classNameInBrowser; } const className = config?.className; if (typeof className === 'string') { return className; } if (production) { console.log('class without @CLASS.NAME deocrator', target); throw new Error(ERROR_MSG_CLASS_WITHOUT_DECORATOR); } else { // Helpers.log('check for ' + target.name) // setTimeout(() => { // // Helpers.log('check for ' + target.name + ' === ' + config.className)/ // // TODO this may work, but not yet in singleton/morphi // if (!config.className) { // if (target?.name && target.name !== 'Object') { // ConfigHelpers.log(`[typescript-class-helpers] Please use @CLASS.NAME` // + `('${(target?.name && !!target.name) ? target.name : '...'}') decorator for class ${target?.name}`) // } // } // }) } // special thing when cloning classes if (target.name?.startsWith('class_')) { return ''; } return target.name; } CLASSNAME_1.getClassName = getClassName; // @ts-ignore function getObjectIndexPropertyValue(obj) { const className = TchHelpers.getNameFromObject(obj); // console.log('className',className) let c = getClasses().find(c => c.className === className); // console.log('c',c) if (c) { return c.uniqueKey; } } CLASSNAME_1.getObjectIndexPropertyValue = getObjectIndexPropertyValue; // @ts-ignore function getClassFamilyByClassName(className) { let c = getClasses().find(c => c.className === className); // console.log('getClasses()', getClasses()) if (c) { return c.classFamily; } } CLASSNAME_1.getClassFamilyByClassName = getClassFamilyByClassName; // @ts-ignore function getObjectClassFamily(obj) { const className = TchHelpers.getNameFromObject(obj); // console.log('className',className) let c = getClasses().find(c => c.className === className); // console.log('c',c) if (c) { return c.classFamily; } } CLASSNAME_1.getObjectClassFamily = getObjectClassFamily; function getObjectIndexValue(obj) { const className = TchHelpers.getNameFromObject(obj); // console.log('className',className) let c = getClasses().find(c => c.className === className); // console.log('c',c) if (c && _.isString(c.uniqueKey)) { return obj[c.uniqueKey]; } } CLASSNAME_1.getObjectIndexValue = getObjectIndexValue; function getClassBy(className) { let res; if (Array.isArray(className)) { if (className.length !== 1) { throw `Mapping error... please use proper class names: { propertyObject: 'MyClassName', propertyWithArray: ['MyClassName'] } `; } className = className[0]; } if (typeof className === 'function') { // TODO QUICK_FIX res = className; } if (className === 'Date') { res = Date; } if (className === 'FormData') { res = FormData; } let c = getClasses().find(c => c.className === className); if (c) { res = c.target; } // console.log(`getClassBy "${className} return \n\n ${res} \n\n`) // @ts-ignore return res; } CLASSNAME_1.getClassBy = getClassBy; })(CLASSNAME || (CLASSNAME = {})); /** * @DEPRECATED * Describe fields assigned in class */ const FRegEx = new RegExp(/(?:this\.)(.+?(?= ))/g); function describeFromClassStringify(target, parent = false) { // @ts-ignore var result = []; if (parent) { var proto = Object.getPrototypeOf(target.prototype); if (proto) { // @ts-ignore result = result.concat(describeFromClassStringify(proto.constructor, parent)); } } const classString = target.toString(); const matches = classString.match(FRegEx) || []; // console.log({ // classString, // }); result = result.concat(matches); return result.map(prop => prop .replace('this.', '') .replace('.', '') .replace(')', '') .replace('(', '')); } /** * Describe fields assigne through @DefaultModelWithMapping decorator * without functions */ function describeByDefaultModelsAndMapping(target) { let res = {}; if (target) { // @ts-ignore if (target[SYMBOL.DEFAULT_MODEL]) { // @ts-ignore Object.keys(target[SYMBOL.DEFAULT_MODEL]) .filter(key => { // @ts-ignore return !_.isFunction(target[SYMBOL.DEFAULT_MODEL][key]); }) .forEach(key => { // @ts-ignore return res[key] = null; }); } // @ts-ignore let mapping = target[SYMBOL.MODELS_MAPPING]; if (_.isArray(mapping)) { mapping.forEach(m => { Object.keys(m) .forEach(key => { // @ts-ignore res[key] = null; }); }); } else if (mapping) { Object.keys(mapping) .forEach(key => { // @ts-ignore res[key] = null; }); } } let propNames = Object.keys(res).filter(f => !!f); propNames = (!propNames.includes('id') ? ['id'] : []).concat(propNames); return propNames; } class TchHelpers { static getBy(className) { return CLASSNAME.getClassBy(className); } static getFromObject(o) { if (_.isUndefined(o) || _.isNull(o)) { return; } if (o.constructor) { return o.constructor; } const p = Object.getPrototypeOf(o); return p && p.constructor; } static getName(target, production = false) { return CLASSNAME.getClassName(target, production); } static getNameFromObject(o) { const fnCLass = this.getFromObject(o); return this.getName(fnCLass); } static getConfigs(target) { return CLASSNAME.getClassConfig(target); } static describeProperites(target) { const d1 = describeFromClassStringify(target); const d2 = describeByDefaultModelsAndMapping(target); let uniq = {}; // @ts-ignore d1.concat(d2).forEach(p => uniq[p] = p); return Object.keys(uniq) .filter(d => !!d) .filter(d => typeof target.prototype[d] !== 'function'); } } const notAllowedAsMethodName = [ 'length', 'name', 'arguments', 'caller', 'constructor', 'apply', 'bind', 'call', 'toString', '__defineGetter__', '__defineSetter__', 'hasOwnProperty', '__lookupGetter__', '__lookupSetter__', 'isPrototypeOf', 'propertyIsEnumerable', 'valueOf', '__proto__', 'toLocaleString', ]; const getMethodsNames = (classOrClassInstance, allMethodsNames = []) => { if (!classOrClassInstance) { return allMethodsNames; } const isClassFunction = _.isFunction(classOrClassInstance); const classFun = (isClassFunction ? classOrClassInstance : Object.getPrototypeOf(classOrClassInstance)); const objectToCheck = isClassFunction ? classOrClassInstance?.prototype : classOrClassInstance; const prototypeObj = Object.getPrototypeOf(objectToCheck || {}); const properties = _.uniq([ ...Object.getOwnPropertyNames(objectToCheck || {}), ...Object.getOwnPropertyNames(prototypeObj || {}), ...Object.keys(objectToCheck || {}), ...Object.keys(prototypeObj || {}), ]) .filter(f => !!f && !notAllowedAsMethodName.includes(f)); properties.filter((methodName) => typeof objectToCheck[methodName] === 'function').forEach(p => allMethodsNames.push(p)); if (!classFun || !classFun.constructor || classFun?.constructor?.name === 'Object') { return allMethodsNames; } return getMethodsNames(Object.getPrototypeOf(classFun), allMethodsNames); }; const CLASS = { NAME: CLASSNAME.CLASSNAME, setName: setClassName, getBy: TchHelpers.getBy, /** * @deprecated */ getSingleton(target) { if (typeof target !== 'function') { console.error(`[typescript-class-helpers][setSingletonObj] Type of target is not a function`); return; } const config = TchHelpers.getConfigs(target)[0]; // console.log(`getSingleton for ${target.name}: `, config.singleton) return config.singleton; }, /** * @deprecated */ setSingletonObj(target, singletonObject) { // console.log('SET SINGLETON', singletonObject) if (typeof target !== 'function') { console.error(`[typescript-class-helpers][setSingletonObj] Type of target is not a function`); return; } if (Array.isArray(singletonObject)) { console.error(`[typescript-class-helpers][setSingletonObj] Singleton instance cant be an array`); return; } if (typeof singletonObject !== 'object') { console.error(`[typescript-class-helpers][setSingletonObj] Singleton instance cant must be a object`); return; } const className = CLASS.getName(target); // console.log(`SINGLETON SET for TARGET ${className}`) const config = TchHelpers.getConfigs(target)[0]; if (config.singleton) { console.warn(`[typescript-class-helpers] You are trying to set singleton of "${className}" second time with different object.`); } config.singleton = singletonObject; }, /** * @deprecated */ getConfigs: TchHelpers.getConfigs, /** * @deprecated */ getConfig: (target) => { return _.first(TchHelpers.getConfigs(target)); }, getMethodsNames(classOrClassInstance) { return getMethodsNames(classOrClassInstance); }, getFromObject: TchHelpers.getFromObject, getName: TchHelpers.getName, getNameFromObject: TchHelpers.getNameFromObject, describeProperites: TchHelpers.describeProperites, /** * @deprecated */ OBJECT: (obj) => { return { get indexValue() { return CLASSNAME.getObjectIndexValue(obj); }, get indexProperty() { return CLASSNAME.getObjectIndexPropertyValue(obj); }, get isClassObject() { if (!_.isObject(obj) || _.isArray(obj) || _.isRegExp(obj) || _.isBuffer(obj) || _.isArrayBuffer(obj)) { return false; } if (_.isDate(obj)) { return true; } const className = CLASS.getNameFromObject(obj); return _.isString(className) && className !== 'Object'; }, isEqual: (anotherObj, compareDeep = false) => { if (!CLASS.OBJECT(obj).isClassObject || !CLASS.OBJECT(anotherObj).isClassObject) { return false; } if (obj === anotherObj) { // console.log(`EQ v1: , v2: - class1 ${CLASS.getNameFromObject(obj)}, class2 ${CLASS.getNameFromObject(anotherObj)}`, obj, anotherObj) return true; } const v1 = CLASSNAME.getObjectIndexValue(obj); const v2 = CLASSNAME.getObjectIndexValue(anotherObj); const f1 = CLASSNAME.getObjectClassFamily(obj); const f2 = CLASSNAME.getObjectClassFamily(anotherObj); const isFamilyDiff = (!_.isString(f1) || !_.isString(f2) || (f1 !== f2)); // console.log(`DIFF FAMILY ${isFamilyDiff} v1: ${CLASSNAME.getObjectClassFamily(obj)}, v2: ${CLASSNAME.getObjectClassFamily(anotherObj)} - class1 ${CLASS.getNameFromObject(obj)}, class2 ${CLASS.getNameFromObject(anotherObj)}`) if (isFamilyDiff) { // console.log(`DIFF v1: ${v1}, v2: ${v2} - class1 ${CLASS.getNameFromObject(obj)}, class2 ${CLASS.getNameFromObject(anotherObj)}`) return false; } if (!CLASS.OBJECT(obj).isClassObject || !CLASS.OBJECT(anotherObj).isClassObject) { // console.log(`NOT CLASS v1: ${v1}, v2: ${v2} - class1 ${CLASS.getNameFromObject(obj)}, class2 ${CLASS.getNameFromObject(anotherObj)}`) return false; } // console.log(`v1: ${v1} ,class ${CLASS.getNameFromObject(obj)} ,prop: ${CLASS.OBJECT(obj).indexProperty}`) // console.log(`v2: ${v2} ,class ${CLASS.getNameFromObject(anotherObj)} ,prop: ${CLASS.OBJECT(anotherObj).indexProperty}`) // console.log(`v1: ${v1}, v2: ${v2} - class1 ${CLASS.getNameFromObject(obj)}, class2 ${CLASS.getNameFromObject(anotherObj)}`) // console.log('') if ((_.isNumber(v1) && _.isNumber(v2)) || (_.isString(v1) && _.isString(v2))) { const res = (v1 === v2); // @ts-ignore return res; } if (compareDeep) { return _.isEqual(v1, v2); } return false; } }; } }; /** * Generated bundle index. Do not edit. */ export { CLASS, Models, SYMBOL, TchHelpers }; //# sourceMappingURL=typescript-class-helpers.mjs.map