redefining filter functions
This commit is contained in:
parent
3743aeacf6
commit
0665ebd155
@ -1,4 +1,4 @@
|
||||
import { ClassDeclaration, Decorator, MethodDeclaration, Node, Project, PropertyDeclaration, SourceFile, Type, InterfaceDeclaration, FunctionDeclaration, ExportedDeclarations } from "ts-morph";
|
||||
import { ClassDeclaration, Decorator, ExportedDeclarations, FunctionDeclaration, InterfaceDeclaration, MethodDeclaration, Node, PropertyDeclaration, SourceFile, Type } from "ts-morph";
|
||||
|
||||
export type TypeInformation = {
|
||||
isBaseType: boolean;
|
||||
@ -25,6 +25,8 @@ export type ParameterInformation = ({
|
||||
name: string;
|
||||
originalCode: string;
|
||||
index: number;
|
||||
authorDescription: string;
|
||||
isOptional: boolean;
|
||||
} & TypeInformation);
|
||||
|
||||
export type MethodInformation = {
|
||||
@ -39,12 +41,24 @@ export type MethodInformation = {
|
||||
head: string;
|
||||
authorDescription: string;
|
||||
}
|
||||
|
||||
|
||||
export type DecoratorInformation = {
|
||||
declaration: MethodDeclaration | PropertyDeclaration | ClassDeclaration,
|
||||
decoratorNames: string[],
|
||||
decorators: Decorator[],
|
||||
decoratorSettings: { [index: string]: { [index: string]: any }}
|
||||
decoratorSettings: { [index: string]: { [index: string]: any } }
|
||||
}
|
||||
|
||||
export type ImportMapping = {
|
||||
mapping: {
|
||||
[index: string]: {
|
||||
importSrc: string;
|
||||
alias?: string;
|
||||
};
|
||||
};
|
||||
aliasToOriginal: {
|
||||
[index: string]: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -203,7 +217,7 @@ function _getType(node: Node, inputType: Type, text: string) {
|
||||
if (result.length > 0) {
|
||||
|
||||
// Update the Imported Types.
|
||||
typeImports.push(... result.map(item => {
|
||||
typeImports.push(...result.map(item => {
|
||||
const text = item.toString();
|
||||
|
||||
// Regex, to extract the path of the Import.
|
||||
@ -234,7 +248,7 @@ function _getType(node: Node, inputType: Type, text: string) {
|
||||
typeImports.push(...defintion.typeImports)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Define the a Simplified Subtype.
|
||||
simplifiedSubType = simplifiedType
|
||||
@ -350,18 +364,27 @@ export function getDescription(declaration: PropertyDeclaration | InterfaceDecla
|
||||
//
|
||||
} else if (Node.isMethodDeclaration(declaration)) {
|
||||
const isAbstract = declaration.isAbstract();
|
||||
const isAsync = declaration.isAsync();
|
||||
const name = declaration.getName();
|
||||
const isGenerator = declaration.isGenerator();
|
||||
const isImplementation = declaration.isImplementation();
|
||||
|
||||
const retTypeObj = declaration.getReturnType();
|
||||
|
||||
const returnType = _getType(
|
||||
declaration.getReturnTypeNode(),
|
||||
retTypeObj,
|
||||
retTypeObj.getText()
|
||||
)
|
||||
);
|
||||
|
||||
// Use the Regex to remove the Imports from the Head
|
||||
const regex = /import\(.+?\)./g
|
||||
// Define the Head
|
||||
let head = declaration.getType().getText().replace(regex, '');
|
||||
head = head.slice(0, head.length - ('=> ' + returnType.simplifiedType).length)
|
||||
|
||||
// Extract the Description of the Author.
|
||||
const authorDescription = declaration.getLeadingCommentRanges().map(comment => comment.getText()).join('\n');
|
||||
// Flag if the Function is Performed Async. (this is achieved by retruning a promise or adding an async tag in the beginning)
|
||||
const isAsync = declaration.isAsync() || returnType.baseType == 'Promise';
|
||||
|
||||
// Extract the Parameters of the Function
|
||||
const params: ParameterInformation[] = declaration.getParameters().map((parameter, index) => {
|
||||
return Object.assign(
|
||||
@ -372,6 +395,10 @@ export function getDescription(declaration: PropertyDeclaration | InterfaceDecla
|
||||
originalCode: parameter.getText(),
|
||||
// The Index of the Parameter
|
||||
index,
|
||||
// Extract the Information provided for the Parameter (If available.)
|
||||
authorDescription: (parameter.getLeadingCommentRanges() || parameter.getTrailingCommentRanges()).map(comment => comment.getText()).join('\n'),
|
||||
// Extract whether the Parameters is Optional or not.
|
||||
isOptional: parameter.isOptional()
|
||||
},
|
||||
_getType(
|
||||
parameter.getTypeNode(),
|
||||
@ -381,15 +408,6 @@ export function getDescription(declaration: PropertyDeclaration | InterfaceDecla
|
||||
)
|
||||
});
|
||||
|
||||
// Use the Regex to remove the Imports from the Head
|
||||
const regex = /import\(.+?\)./g
|
||||
// Define the Head
|
||||
let head = declaration.getType().getText().replace(regex, '');
|
||||
head = head.slice(0, head.length - ('=> '+ returnType.simplifiedType).length)
|
||||
|
||||
// Extract the Description of the Author.
|
||||
const authorDescription = declaration.getLeadingCommentRanges().map(comment => comment.getText()).join('\n');
|
||||
|
||||
return {
|
||||
declaration,
|
||||
params,
|
||||
@ -411,13 +429,8 @@ export function getDescription(declaration: PropertyDeclaration | InterfaceDecla
|
||||
* @param reqType The requested Type of the Element
|
||||
* @param caseSensitive A Flage to use casesesitivy during the checkoup
|
||||
*/
|
||||
export function getMatchingProperties(cl: ClassDeclaration, reqType: string, caseSensitive = true): PropertyInformation[] {
|
||||
export function getMatchingProperties(cl: ClassDeclaration): PropertyInformation[] {
|
||||
return cl.getProperties()
|
||||
// Firstly Filter the Properties, that they match the requested Type.
|
||||
.filter(prop => isPropOfType(
|
||||
prop,
|
||||
reqType,
|
||||
caseSensitive))
|
||||
// Instead of returning the Property Declaration, return the
|
||||
// Property Descriptor.
|
||||
.map(
|
||||
@ -440,7 +453,7 @@ export function getDecorators(declaration: MethodDeclaration | PropertyDeclarati
|
||||
declaration,
|
||||
decoratorNames: [],
|
||||
decoratorSettings: {},
|
||||
decorators:[]
|
||||
decorators: []
|
||||
}
|
||||
|
||||
if (!caseSensitive) {
|
||||
@ -451,7 +464,7 @@ export function getDecorators(declaration: MethodDeclaration | PropertyDeclarati
|
||||
.filter(usedDecorator => {
|
||||
// Get the Name of the Decorator
|
||||
let nameOfDecorator = usedDecorator.getName();
|
||||
|
||||
|
||||
// Let CaseSensitive or not
|
||||
if (!caseSensitive) {
|
||||
nameOfDecorator = nameOfDecorator.toLowerCase();
|
||||
@ -465,7 +478,7 @@ export function getDecorators(declaration: MethodDeclaration | PropertyDeclarati
|
||||
_arguments.map(a => {
|
||||
// Parse the Text
|
||||
const text = a.getText();
|
||||
|
||||
|
||||
// Bad Practice. Create the Code create Function that will create the Object.
|
||||
try {
|
||||
ret.decoratorSettings[nameOfDecorator] = eval('() => { return ' + text + '}')();
|
||||
@ -557,6 +570,49 @@ export function getModifiers(declaration: MethodDeclaration | PropertyDeclaratio
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to create a Default Filter for Methods.
|
||||
* @param options
|
||||
*/
|
||||
export function defaultClassFilter(options: {
|
||||
classDecorator: string,
|
||||
classInterface: string,
|
||||
}) {
|
||||
return (cl: ClassDeclaration, importMapping: ImportMapping) => {
|
||||
return (
|
||||
options.classInterface === '' ||
|
||||
isClassImplementingInterface(cl, options.classInterface, importMapping.aliasToOriginal)
|
||||
) && (
|
||||
options.classDecorator === '' ||
|
||||
getDecorators(cl, options.classDecorator, importMapping.aliasToOriginal)
|
||||
) as boolean
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default Method for filtering Methods.
|
||||
*/
|
||||
export function defaultMethodFilter() {
|
||||
return (cl: ClassDeclaration, method: (MethodInformation & DecoratorInformation & ModifierInformation), importMapping: ImportMapping) => {
|
||||
return method.isPublic && method.isAsync;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default Filter, to Filter Properties
|
||||
* @param options The Options to use.
|
||||
*/
|
||||
export function defaultPropFilter(options: {
|
||||
propertyType: string,
|
||||
propertyDecorator: string,
|
||||
}) {
|
||||
return (cl: ClassDeclaration, property: (PropertyInformation & DecoratorInformation & ModifierInformation), importMapping: ImportMapping) => {
|
||||
return isPropOfType(property.declaration, options.propertyType, false) &&
|
||||
property.isPublic &&
|
||||
property.decoratorNames.includes(options.propertyDecorator)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper Function to List relevant classes with their corresponding elements
|
||||
* @param sources The Source Files
|
||||
@ -567,18 +623,19 @@ export function getModifiers(declaration: MethodDeclaration | PropertyDeclaratio
|
||||
* @param propertyDecorator The Decorator for the Property
|
||||
*/
|
||||
export function analyzeClasses(sources: SourceFile[], options: {
|
||||
filterClasses: (cl: ClassDeclaration, importMapping: ImportMapping) => boolean,
|
||||
filterMethods: (cl: ClassDeclaration, method: (MethodInformation & DecoratorInformation & ModifierInformation), importMapping: ImportMapping) => boolean,
|
||||
filterProperties: (cl: ClassDeclaration, property: (PropertyInformation & DecoratorInformation & ModifierInformation), importMapping: ImportMapping) => boolean,
|
||||
classDecorator: string,
|
||||
classInterface: string,
|
||||
methodDecorator: string,
|
||||
propertyType: string,
|
||||
propertyDecorator: string,
|
||||
}) {
|
||||
|
||||
const ret: {
|
||||
className: string;
|
||||
decorator: DecoratorInformation,
|
||||
methods: (MethodInformation & DecoratorInformation)[];
|
||||
properties: (PropertyInformation & DecoratorInformation) [];
|
||||
methods: (MethodInformation & DecoratorInformation & ModifierInformation)[];
|
||||
properties: (PropertyInformation & DecoratorInformation & ModifierInformation)[];
|
||||
}[] = [];
|
||||
|
||||
// Iterate over the Files:
|
||||
@ -588,15 +645,7 @@ export function analyzeClasses(sources: SourceFile[], options: {
|
||||
const importMapping = getImportsOfFile(file);
|
||||
|
||||
// After all Imports has been detected => filter for all Classes that implement the provided classDecorator
|
||||
const relevantClasses = file.getClasses().filter(cl => {
|
||||
return (
|
||||
options.classInterface === '' ||
|
||||
isClassImplementingInterface(cl, options.classInterface, importMapping.aliasToOriginal)
|
||||
) && (
|
||||
options.classDecorator === '' ||
|
||||
getDecorators(cl, options.classDecorator, importMapping.aliasToOriginal)
|
||||
)
|
||||
});
|
||||
const relevantClasses = file.getClasses().filter(cl => options.filterClasses(cl, importMapping));
|
||||
|
||||
// Now after each class is known => ierate over the relevant classes
|
||||
// and get their relevant Methods and Attributes.
|
||||
@ -604,35 +653,42 @@ export function analyzeClasses(sources: SourceFile[], options: {
|
||||
// Extract the Methods.
|
||||
const sharedMethodsOfClass = relevantClass.getMethods()
|
||||
.map(method => getDecorators(method, options.methodDecorator, importMapping.aliasToOriginal))
|
||||
.filter(methodObject => methodObject.decorators.length > 0 && getModifiers(methodObject.declaration as MethodDeclaration).isPublic);
|
||||
.filter(methodObject => methodObject.decorators.length > 0);
|
||||
|
||||
// Parsed Method
|
||||
const parsedMethods = sharedMethodsOfClass.map(methodObject => Object.assign(
|
||||
getDescription(methodObject.declaration as MethodDeclaration) as MethodInformation,
|
||||
getDecorators(
|
||||
methodObject.declaration,
|
||||
options.methodDecorator,
|
||||
importMapping.aliasToOriginal
|
||||
)
|
||||
));
|
||||
const parsedMethods = sharedMethodsOfClass
|
||||
.map(methodObject =>
|
||||
Object.assign(
|
||||
getDescription(methodObject.declaration as MethodDeclaration) as MethodInformation,
|
||||
getDecorators(
|
||||
methodObject.declaration,
|
||||
options.methodDecorator,
|
||||
importMapping.aliasToOriginal
|
||||
),
|
||||
getModifiers(methodObject.declaration as MethodDeclaration)
|
||||
)
|
||||
).filter(item => options.filterMethods(
|
||||
relevantClass,
|
||||
item,
|
||||
importMapping
|
||||
));
|
||||
|
||||
// Get the Properties
|
||||
const relevantProperties = getMatchingProperties(relevantClass, options.propertyType, false)
|
||||
.map(p => Object.assign(
|
||||
p,
|
||||
getDecorators(
|
||||
p.declaration,
|
||||
options.propertyDecorator,
|
||||
importMapping.aliasToOriginal,
|
||||
false,
|
||||
true
|
||||
))
|
||||
)
|
||||
.filter(
|
||||
property =>
|
||||
property.isPublic &&
|
||||
property.decoratorNames.includes(options.propertyDecorator)
|
||||
const relevantProperties = getMatchingProperties(relevantClass)
|
||||
.map(property =>
|
||||
Object.assign(
|
||||
property,
|
||||
getDecorators(
|
||||
property.declaration,
|
||||
options.propertyDecorator,
|
||||
importMapping.aliasToOriginal,
|
||||
false,
|
||||
true
|
||||
),
|
||||
getModifiers(property.declaration)
|
||||
)
|
||||
).filter(property => options.filterProperties(relevantClass, property, importMapping)
|
||||
)
|
||||
|
||||
const item = {
|
||||
decorator: getDecorators(
|
||||
@ -749,25 +805,35 @@ export function getDeclarationByName(files: { [index: string]: SourceFile }, fil
|
||||
* @param options Options, to Controll the generation.
|
||||
*/
|
||||
export function analyzeFiles(sourceFiles: SourceFile[], options: {
|
||||
classDecorator: string,
|
||||
classInterface: string,
|
||||
methodDecorator: string,
|
||||
propertyType: string,
|
||||
filterClasses: (cl: ClassDeclaration, importMapping: ImportMapping) => boolean,
|
||||
filterMethods: (cl: ClassDeclaration, method: (MethodInformation & DecoratorInformation & ModifierInformation), importMapping: ImportMapping) => boolean,
|
||||
filterProperties: (cl: ClassDeclaration, property: (PropertyInformation & DecoratorInformation & ModifierInformation), importMapping: ImportMapping) => boolean,
|
||||
checkImport: (type: string) => boolean,
|
||||
classDecorator: string,
|
||||
methodDecorator: string,
|
||||
propertyDecorator: string,
|
||||
} = {
|
||||
classDecorator: 'exportsElementsToDispatcher',
|
||||
classInterface: '',
|
||||
methodDecorator: 'exportMethodToDispatcher',
|
||||
propertyType: 'nopeObservable',
|
||||
propertyDecorator: 'exportPropertyToDispatcher'
|
||||
}){
|
||||
classDecorator: 'exportsElementsToDispatcher',
|
||||
methodDecorator: 'exportMethodToDispatcher',
|
||||
propertyDecorator: 'exportPropertyToDispatcher',
|
||||
filterClasses: defaultClassFilter({
|
||||
classDecorator: 'exportsElementsToDispatcher',
|
||||
classInterface: ''
|
||||
}),
|
||||
filterMethods: defaultMethodFilter(),
|
||||
filterProperties: defaultPropFilter({
|
||||
propertyDecorator: 'exportPropertyToDispatcher',
|
||||
propertyType: 'nopeObservable'
|
||||
}),
|
||||
checkImport: type => type !== 'nopeObservable'
|
||||
}) {
|
||||
const fileMapping = createFileMapping(sourceFiles);
|
||||
const classes = analyzeClasses(sourceFiles, options);
|
||||
|
||||
const ret: IAnalyzeResult[] = []
|
||||
const ret: IAnalyzeResult[] = []
|
||||
|
||||
// Iterate over the Classes
|
||||
for (const relevantClass of classes){
|
||||
for (const relevantClass of classes) {
|
||||
|
||||
const item: IAnalyzeResult = {
|
||||
className: relevantClass.className,
|
||||
@ -786,19 +852,19 @@ export function analyzeFiles(sourceFiles: SourceFile[], options: {
|
||||
} = {}
|
||||
|
||||
// Iterate over the Properties
|
||||
for (const prop of relevantClass.properties){
|
||||
if (!prop.isBaseType){
|
||||
for (const {identifier: type, path} of (prop.typeImports || [])){
|
||||
for (const prop of relevantClass.properties) {
|
||||
if (!prop.isBaseType) {
|
||||
for (const { identifier, path } of (prop.typeImports || [])) {
|
||||
|
||||
// Only if the import isnt the Base Type, add it to the List.
|
||||
if (type !== options.propertyType) {
|
||||
if (options.checkImport(identifier)) {
|
||||
// Check if the Imported Type has been Adden multiple Times.
|
||||
if (mappingTypeToImport[type] === undefined) {
|
||||
mappingTypeToImport[type] = new Set<string>();
|
||||
if (mappingTypeToImport[identifier] === undefined) {
|
||||
mappingTypeToImport[identifier] = new Set<string>();
|
||||
}
|
||||
|
||||
mappingTypeToImport[type].add(path);
|
||||
requiredImports.add(type);
|
||||
mappingTypeToImport[identifier].add(path);
|
||||
requiredImports.add(identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -807,12 +873,12 @@ export function analyzeFiles(sourceFiles: SourceFile[], options: {
|
||||
}
|
||||
|
||||
// Iterate over the Methods
|
||||
for (const method of relevantClass.methods){
|
||||
for (const method of relevantClass.methods) {
|
||||
if (!method.returnType.isBaseType) {
|
||||
for (const {identifier, path} of (method.returnType.typeImports || [])){
|
||||
for (const { identifier, path } of (method.returnType.typeImports || [])) {
|
||||
|
||||
// Only if the import isnt the Base Type, add it to the List.
|
||||
if (identifier !== options.propertyType) {
|
||||
if (options.checkImport(identifier)) {
|
||||
// Check if the Imported Type has been Adden multiple Times.
|
||||
if (mappingTypeToImport[identifier] === undefined) {
|
||||
mappingTypeToImport[identifier] = new Set<string>();
|
||||
@ -827,17 +893,17 @@ export function analyzeFiles(sourceFiles: SourceFile[], options: {
|
||||
// Iterate over the Parameters and extract
|
||||
// the required elements. (If they arent base
|
||||
// types).
|
||||
for (const parm of method.params){
|
||||
if (!parm.isBaseType){
|
||||
for (const {identifier, path} of (parm.typeImports || [])){
|
||||
for (const parm of method.params) {
|
||||
if (!parm.isBaseType) {
|
||||
for (const { identifier, path } of (parm.typeImports || [])) {
|
||||
|
||||
// Only if the import isnt the Base Type, add it to the List.
|
||||
if (identifier !== options.propertyType) {
|
||||
if (options.checkImport(identifier)) {
|
||||
// Check if the Imported Type has been Adden multiple Times.
|
||||
if (mappingTypeToImport[identifier] === undefined) {
|
||||
mappingTypeToImport[identifier] = new Set<string>();
|
||||
}
|
||||
|
||||
|
||||
mappingTypeToImport[identifier].add(path);
|
||||
requiredImports.add(identifier);
|
||||
}
|
||||
@ -866,7 +932,7 @@ export function analyzeFiles(sourceFiles: SourceFile[], options: {
|
||||
item.imports.content += declarations.map(item =>
|
||||
item.originalCode
|
||||
).reduce(
|
||||
(prev, current, idx) => item.imports.content.length === 0 && idx === 0? current : prev + '\n\n' + current, ''
|
||||
(prev, current, idx) => item.imports.content.length === 0 && idx === 0 ? current : prev + '\n\n' + current, ''
|
||||
);
|
||||
|
||||
} else if (mappingTypeToImport[reqType].size > 1) {
|
||||
|
Loading…
Reference in New Issue
Block a user