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 = {
|
||||
@ -47,6 +49,18 @@ export type DecoratorInformation = {
|
||||
decoratorSettings: { [index: string]: { [index: string]: any } }
|
||||
}
|
||||
|
||||
export type ImportMapping = {
|
||||
mapping: {
|
||||
[index: string]: {
|
||||
importSrc: string;
|
||||
alias?: string;
|
||||
};
|
||||
};
|
||||
aliasToOriginal: {
|
||||
[index: string]: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for the Analyzing Result
|
||||
*/
|
||||
@ -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(
|
||||
@ -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,34 +653,41 @@ 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(
|
||||
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,
|
||||
const relevantProperties = getMatchingProperties(relevantClass)
|
||||
.map(property =>
|
||||
Object.assign(
|
||||
property,
|
||||
getDecorators(
|
||||
p.declaration,
|
||||
property.declaration,
|
||||
options.propertyDecorator,
|
||||
importMapping.aliasToOriginal,
|
||||
false,
|
||||
true
|
||||
))
|
||||
),
|
||||
getModifiers(property.declaration)
|
||||
)
|
||||
.filter(
|
||||
property =>
|
||||
property.isPublic &&
|
||||
property.decoratorNames.includes(options.propertyDecorator)
|
||||
).filter(property => options.filterProperties(relevantClass, property, importMapping)
|
||||
)
|
||||
|
||||
const item = {
|
||||
@ -749,17 +805,27 @@ export function getDeclarationByName(files: { [index: string]: SourceFile }, fil
|
||||
* @param options Options, to Controll the generation.
|
||||
*/
|
||||
export function analyzeFiles(sourceFiles: 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,
|
||||
checkImport: (type: string) => boolean,
|
||||
classDecorator: string,
|
||||
classInterface: string,
|
||||
methodDecorator: string,
|
||||
propertyType: string,
|
||||
propertyDecorator: string,
|
||||
} = {
|
||||
classDecorator: 'exportsElementsToDispatcher',
|
||||
classInterface: '',
|
||||
methodDecorator: 'exportMethodToDispatcher',
|
||||
propertyType: 'nopeObservable',
|
||||
propertyDecorator: 'exportPropertyToDispatcher'
|
||||
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);
|
||||
@ -788,17 +854,17 @@ 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 { 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -812,7 +878,7 @@ export function analyzeFiles(sourceFiles: SourceFile[], options: {
|
||||
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>();
|
||||
@ -832,7 +898,7 @@ export function analyzeFiles(sourceFiles: SourceFile[], options: {
|
||||
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>();
|
||||
|
Loading…
Reference in New Issue
Block a user