grunt-generate-view-model
Version:
Global task for generation view model
396 lines • 21.1 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDependencyImportsForImports = exports.mapFileClasses = exports.filterFileMetadata = exports.createFieldMetadata = exports.updateFieldMetadataForViewModelTypeDecorator = exports.updateFieldMetadataForViewModelNameDecorator = exports.updateFieldMetadataForIgnoreViewModelDecorator = exports.updateFieldMetadataForIgnoreDecorators = exports.getInfoFromImports = exports.createClassMeta = void 0;
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
const fs = __importStar(require("fs"));
const ts_file_parser_1 = require("ts-file-parser");
const _1 = require(".");
const decorators_1 = require("../model/decorators");
const enums_1 = require("./enums");
const classmetadata_1 = require("../model/classmetadata");
const fieldmetadata_1 = require("../model/fieldmetadata");
const pipes_1 = require("../pipes");
const ignoreDecorators_1 = require("../../tasks/constants/ignoreDecorators");
const transformer_1 = require("./transformer");
const arrayType = '[]';
const baseTypes = ['string', 'number', 'boolean', 'undefined', 'null', 'object'];
const filterOnlyIgnoreDecorators = (dec) => dec.name === decorators_1.Decorators.IgnoreDecorators;
const whiteListClassDecorators = (dec) => !ignoreDecorators_1.ignoreDecorators.includes(dec.name);
const addExtendClassFieldsToClass = (classExtends, baseClass, imports, { currentExtendLevel, maxExtendedLevel }) => {
const fields = [];
classExtends.forEach((extend) => {
const importBaseClass = imports.find(importNode => importNode.clauses.find(clause => clause === extend.basicName));
const extendFileString = fs.readFileSync(`${importBaseClass.absPathString}.ts`, { encoding: 'utf-8' });
const extendFileStructure = ts_file_parser_1.parseStruct(extendFileString, {}, importBaseClass.absPathString);
const extendBaseClass = extendFileStructure.classes.find(possibleClass => possibleClass.name === extend.basicName);
if (!extendBaseClass) {
return;
}
extendBaseClass.fields.forEach(f => {
const fieldExistInCreatedClass = baseClass.fields.find(ff => ff.name === f.name);
if (fieldExistInCreatedClass) {
return;
}
// side effect - update base imports to correct import view file;
imports.push(...(extendFileStructure._imports || []));
fields.push(f);
});
if (currentExtendLevel < maxExtendedLevel && extendBaseClass.extends) {
fields.push(...addExtendClassFieldsToClass(extendBaseClass.extends, baseClass, imports, {
currentExtendLevel: currentExtendLevel + 1,
maxExtendedLevel
}));
}
});
return fields;
};
const createClassMeta = (baseClass_, { type, mapperPath, model }, imports, path, maxExtendedLevel) => {
const baseClass = Object.assign({}, baseClass_);
const classMeta = new classmetadata_1.ClassMetadata();
classMeta.name = _1.upFirstLetter(model);
classMeta.fields = [];
classMeta.generateView = true;
if (mapperPath) {
classMeta.needMapper = true;
}
classMeta.type = type || 'interface';
classMeta.baseName = baseClass.name;
classMeta.baseNamePath = path;
const options = {
currentExtendLevel: 1,
maxExtendedLevel
};
baseClass.fields.push(...addExtendClassFieldsToClass(baseClass.extends, baseClass_, imports, options));
classMeta.fields = baseClass.fields.map(fld => exports.createFieldMetadata(fld, classMeta, imports));
classMeta.decorators = [];
const ignoreClassDecorators = baseClass.decorators.filter(filterOnlyIgnoreDecorators);
let otherClassDecorators = baseClass.decorators.filter(whiteListClassDecorators);
if (ignoreClassDecorators.length) {
ignoreClassDecorators.forEach(ignoreClassDecorator => {
const [decoratorsToIgnore, modelsWhereDecoratorWillIgnore] = ignoreClassDecorator.arguments;
const modelInBlackList = (modelsWhereDecoratorWillIgnore || []).includes(model);
const classDecorators = otherClassDecorators.filter(d => {
const decoratorInBlackList = (decoratorsToIgnore || []).includes(d.name);
return !(decoratorInBlackList && modelInBlackList);
});
otherClassDecorators = classDecorators;
});
}
classMeta.decorators = otherClassDecorators;
const fieldsWithConvertFunctions = classMeta.fields.filter(f => f.fieldConvertFunction);
fieldsWithConvertFunctions.forEach(f => {
const func = f.fieldConvertFunction;
transformer_1.saveInfoAboutTransformer(enums_1.FuncDirection.toView, func, imports, classMeta);
transformer_1.saveInfoAboutTransformer(enums_1.FuncDirection.fromView, func, imports, classMeta);
});
return classMeta;
};
exports.createClassMeta = createClassMeta;
const getInfoFromImports = (imports, typeName) => {
let isComplexType = true;
let isEnum = false;
imports.forEach(repeatImport => {
if (repeatImport.isNodeModule) {
return;
}
repeatImport.clauses.forEach(cl => {
if (cl !== typeName) {
return;
}
const fileName = repeatImport.absPathNode.join('/') + '.ts';
const fileContent = fs.readFileSync(fileName, 'utf-8');
const parsedFile = ts_file_parser_1.parseStruct(fileContent, {}, fileName);
if (!parsedFile.enumDeclarations) {
return;
}
const isEnumType = !!parsedFile.enumDeclarations.find(enm => enm.name === typeName);
isComplexType = !isEnumType;
isEnum = !!isEnumType;
});
});
return {
isComplexType,
isEnum
};
};
exports.getInfoFromImports = getInfoFromImports;
const updateFieldMetadataForIgnoreDecorators = (ignoreDecorators_, classMeta, fldMetadata, decoratorsOnField, imports) => {
const newFldMetadata = Object.assign({}, fldMetadata);
const ignoreAllDecoratorsExist = ignoreDecorators_.find(d => { var _a; return ((_a = d.arguments) === null || _a === void 0 ? void 0 : _a.length) === 0; });
const ignoreDecoratorsForFieldArray = ignoreDecorators_.map(dec => dec.arguments[0]) || [];
const allIgnoreCustomFields = [];
ignoreDecoratorsForFieldArray.forEach((d) => allIgnoreCustomFields.push(...(d || [])));
const fieldIgnoreDecorators = [...allIgnoreCustomFields, ...ignoreDecorators_1.ignoreDecorators];
const fieldIgnoreDecoratorsClasses = (ignoreDecorators_.map(dec => dec.arguments[1]) || []);
if (!decoratorsOnField || !decoratorsOnField.length) {
newFldMetadata.decorators = [];
return {
fieldMetadata: newFldMetadata,
possibleImports: [],
};
}
const possibleImports = [];
newFldMetadata.decorators = decoratorsOnField.filter(dec => {
if (ignoreAllDecoratorsExist) {
return false;
}
const isDecoratorAvailableForField = !fieldIgnoreDecorators.includes(dec.name);
const isDecoratorAvailableForClass = !fieldIgnoreDecoratorsClasses.includes(classMeta.name);
return isDecoratorAvailableForClass && isDecoratorAvailableForField;
});
newFldMetadata.decorators.forEach(dec => {
const importNode = imports.find(_import => _import.clauses.find(cl => cl === dec.name));
if (!importNode) {
return;
}
possibleImports.push(importNode);
});
newFldMetadata.decorators = newFldMetadata.decorators.map(d => {
const updatedDecorator = Object.assign({}, d);
if (updatedDecorator.arguments && updatedDecorator.arguments.length) {
updatedDecorator.arguments = updatedDecorator.arguments.map(a => {
if (typeof a === 'object') {
return JSON.stringify(a);
}
return a;
});
}
return updatedDecorator;
});
return {
fieldMetadata: newFldMetadata,
possibleImports,
};
};
exports.updateFieldMetadataForIgnoreDecorators = updateFieldMetadataForIgnoreDecorators;
const updateFieldMetadataForIgnoreViewModelDecorator = (decorators, classMeta, fldMetadata) => {
if (!decorators || !decorators.length) {
return fldMetadata;
}
const updatedFldMetadata = Object.assign({}, fldMetadata);
decorators.forEach(decorator => {
const ignoreViewModelName = decorator.arguments[0];
const setIgnoreNameToClass = ignoreViewModelName && ignoreViewModelName.toString() === classMeta.name;
const ignoredInView = setIgnoreNameToClass || !ignoreViewModelName;
if (ignoredInView && !updatedFldMetadata.ignoredInView) {
updatedFldMetadata.ignoredInView = true;
}
});
return updatedFldMetadata;
};
exports.updateFieldMetadataForIgnoreViewModelDecorator = updateFieldMetadataForIgnoreViewModelDecorator;
const updateFieldMetadataForViewModelNameDecorator = (decorators, classMeta, fldMetadata) => {
if (!decorators || !decorators.length) {
return fldMetadata;
}
const updatedFldMetadata = Object.assign({}, fldMetadata);
decorators.forEach(decorator => {
const fieldName = decorator.arguments[0];
const modelName = decorator.arguments[1];
const modelNameSetForClass = modelName && modelName.toString() === classMeta.name;
if (modelNameSetForClass || !modelName) {
updatedFldMetadata.name = fieldName.toString();
}
});
return updatedFldMetadata;
};
exports.updateFieldMetadataForViewModelNameDecorator = updateFieldMetadataForViewModelNameDecorator;
const updateFieldMetadataForViewModelTypeDecorator = (decorators, classMeta, flMetadata, imports) => {
try {
if (!decorators || !decorators.length) {
return { fieldMetadata: flMetadata, possibleImports: [] };
}
const updatedFieldMetadata = Object.assign({}, flMetadata);
const possibleImports = [];
const decorator = decorators.find(decorator_ => {
const decoratorArguments = decorator_.arguments[0].valueOf();
return !decoratorArguments.modelName || decoratorArguments.modelName === classMeta.name;
});
if (!decorator) {
return { fieldMetadata: updatedFieldMetadata, possibleImports };
}
const fieldTypeOptions = decorator.arguments[0].valueOf();
updatedFieldMetadata.nullable = !(fieldTypeOptions && typeof fieldTypeOptions.nullable === 'boolean' && !fieldTypeOptions.nullable);
updatedFieldMetadata.type = fieldTypeOptions.type.toString();
const isArray = updatedFieldMetadata.type.indexOf(arrayType) > -1;
updatedFieldMetadata.isArray = isArray;
updatedFieldMetadata.type = isArray
? updatedFieldMetadata.type.substring(0, updatedFieldMetadata.type.indexOf(arrayType))
: updatedFieldMetadata.type;
if (updatedFieldMetadata.type.toLowerCase() === 'string' && updatedFieldMetadata.type !== updatedFieldMetadata.baseModelType) {
updatedFieldMetadata.type = 'string';
updatedFieldMetadata.toStringWanted = true;
}
if (!fieldTypeOptions.transformer) {
imports.forEach(i => {
const filteredClauses = i.clauses.filter(clause => clause === updatedFieldMetadata.baseModelType);
filteredClauses.forEach(clause => {
if (i.isNodeModule) {
const impNode = { isNodeModule: true, clauses: i.clauses, absPathNode: i.absPathNode, absPathString: i.absPathNode.join('/') };
possibleImports.push(impNode);
return;
}
const path = `${(i.absPathNode || []).join('/')}.ts`;
const content = fs.readFileSync(path, 'utf-8');
const innerJsonStructure = ts_file_parser_1.parseStruct(content, {}, '');
const filteredClasses = innerJsonStructure.classes.filter(c => c.name === updatedFieldMetadata.baseModelType);
filteredClasses.forEach(c => {
const generateViewDecorators = c.decorators.filter(d => d.name === decorators_1.Decorators.GenerateView);
const sameModelNameDecorators = generateViewDecorators.filter(d => {
const generateOptions = d.arguments[0].valueOf();
const baseType = fieldTypeOptions.type.toString();
const viewModelType = fieldTypeOptions.type.toString().indexOf(arrayType) > -1
? baseType.substring(0, fieldTypeOptions.type.toString().indexOf(arrayType))
: baseType;
return generateOptions.model.toLowerCase() === viewModelType.toLowerCase();
});
sameModelNameDecorators.forEach(d => {
const generateOptions = d.arguments[0].valueOf();
const fileName = `${_1.upFirstLetter(generateOptions.model)}Mapper`;
const mapperImport = {
isNodeModule: false,
clauses: [fileName],
absPathNode: [`${generateOptions.mapperPath}/${fileName}`],
absPathString: `${generateOptions.mapperPath}/${fileName}`
};
updatedFieldMetadata.needGeneratedMapper = true;
possibleImports.push(mapperImport);
});
});
});
});
}
const transformerExistAndNoNeedMapper = fieldTypeOptions.transformer && !updatedFieldMetadata.needGeneratedMapper;
if (!transformerExistAndNoNeedMapper) {
return { fieldMetadata: updatedFieldMetadata, possibleImports };
}
updatedFieldMetadata.fieldConvertFunction = !updatedFieldMetadata.ignoredInView
? fieldTypeOptions.transformer
: null;
return { fieldMetadata: updatedFieldMetadata, possibleImports };
}
catch (e) {
console.error(enums_1.ConsoleColor.Red, `Generate View: Error: updateFieldMetadataForViewModelTypeDecorator: ${e.message}`);
throw e;
}
};
exports.updateFieldMetadataForViewModelTypeDecorator = updateFieldMetadataForViewModelTypeDecorator;
// dirty function - update possible imports;
const createFieldMetadata = (field, cm, imports) => {
let fldMetadata = new fieldmetadata_1.FieldMetadata();
fldMetadata.baseModelName = field.name;
fldMetadata.nullable = true;
if (field.type.typeKind === ts_file_parser_1.TypeKind.ARRAY) {
fldMetadata.isArray = true;
}
const typeInfo = field.type.base || field.type;
let currentBase = field.type.base;
fldMetadata.baseModelType = typeInfo.typeName;
while (currentBase && currentBase.base !== undefined) {
currentBase = currentBase.base;
fldMetadata.baseModelType = currentBase.typeName;
}
const typeName = fldMetadata.baseModelType;
const isBaseTypesIncludeTypeName = baseTypes.find(type => type === typeName);
if (!isBaseTypesIncludeTypeName) {
const fldInfo = exports.getInfoFromImports(imports, typeName);
fldMetadata.isComplexType = fldInfo.isComplexType;
fldMetadata.isEnum = fldInfo.isEnum;
}
fldMetadata.name = field.name;
fldMetadata.type = fldMetadata.baseModelType;
const fieldDecorators = field.decorators;
fldMetadata = exports.updateFieldMetadataForIgnoreViewModelDecorator(fieldDecorators.filter(decorator => decorator.name === decorators_1.Decorators.IgnoreViewModel), cm, fldMetadata);
fldMetadata = exports.updateFieldMetadataForViewModelNameDecorator(fieldDecorators.filter(decorator => decorator.name === decorators_1.Decorators.ViewModelName), cm, fldMetadata);
const _a = exports.updateFieldMetadataForViewModelTypeDecorator(fieldDecorators.filter(decorator => decorator.name === decorators_1.Decorators.ViewModelType), cm, fldMetadata, imports), { fieldMetadata } = _a, rest = __rest(_a, ["fieldMetadata"]);
fldMetadata = fieldMetadata;
imports.push(...rest.possibleImports);
const decUpdate = exports.updateFieldMetadataForIgnoreDecorators(fieldDecorators === null || fieldDecorators === void 0 ? void 0 : fieldDecorators.filter(decorator => decorator.name === decorators_1.Decorators.IgnoreDecorators), cm, fldMetadata, fieldDecorators === null || fieldDecorators === void 0 ? void 0 : fieldDecorators.filter(decorator => !ignoreDecorators_1.ignoreDecorators.includes(decorator.name)), imports);
fldMetadata = decUpdate.fieldMetadata;
if (fldMetadata.type !== fldMetadata.baseModelType && !baseTypes.includes(fldMetadata.type)) {
const fldInfo = exports.getInfoFromImports(imports, fldMetadata.type);
fldMetadata.isComplexType = fldInfo.isComplexType;
fldMetadata.isEnum = fldInfo.isEnum;
}
imports.push(...decUpdate.possibleImports);
return fldMetadata;
};
exports.createFieldMetadata = createFieldMetadata;
const filterFileMetadata = (imports, classes) => {
const newImports = imports.filter(imp => {
const importArray = imp.type.slice(1, imp.type.length - 1).trim().split(',');
return !importArray.find(_imp => _imp === classes.baseName);
});
return newImports;
};
exports.filterFileMetadata = filterFileMetadata;
const mapFileClasses = (classes, fileMetadata) => {
const newClass = Object.assign({}, classes);
newClass.viewModelFromMapper = _1.getModelNameFromPath(fileMetadata.mapperPath, fileMetadata.filename);
newClass.baseModelFromMapper = _1.getModelNameFromPath(fileMetadata.mapperPath, fileMetadata.basePath);
return newClass;
};
exports.mapFileClasses = mapFileClasses;
const getDependencyImportsForImports = (_imports, fileMetadata) => {
const mapperFileRegexp = /[a-zA-Z]+Mapper/;
let imports = [...(_imports || [])];
imports = imports.reduce((prev, cur) => {
const curType = cur.type;
if (prev.find(i => i.type === curType)) {
return [...prev];
}
return [...prev, cur];
}, []).map(imp => {
let dependencyMappers = [];
// eslint-disable-next-line @typescript-eslint/prefer-regexp-exec
const mapperMatch = imp.type.match(mapperFileRegexp);
if (!mapperMatch) {
imp.dependencyMappers = [];
return null;
}
const mapperName = mapperMatch[0];
fileMetadata.classMetadata.fields.forEach(field => {
const condition = mapperName.includes(field.type) && field.needGeneratedMapper && !field.ignoredInView;
if (!condition) {
return;
}
dependencyMappers.push(field.type);
});
dependencyMappers = pipes_1.unique(dependencyMappers);
imp.dependencyMappers = dependencyMappers;
return imp;
}).filter(i => !!i);
return imports;
};
exports.getDependencyImportsForImports = getDependencyImportsForImports;
//# sourceMappingURL=classmeta.js.map