mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-03-21 14:12:11 +00:00
feat: modernize core-tools
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2019 StApps
|
||||
* Copyright (C) 2021 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
@@ -15,12 +15,13 @@
|
||||
import {Logger} from '@openstapps/logger';
|
||||
import {createWriteStream} from 'fs';
|
||||
import * as request from 'got';
|
||||
import {getFullTypeName} from '../common';
|
||||
import {LightweightClassDefinition} from './model/lightweight-class-definition';
|
||||
import {LightweightDefinition} from './model/lightweight-definition';
|
||||
import {LightweightEnumDefinition} from './model/lightweight-enum-definition';
|
||||
import {LightweightProperty} from './model/lightweight-property';
|
||||
import {LightweightType} from './model/lightweight-type';
|
||||
import {forEach, map, isEmpty} from 'lodash';
|
||||
import {expandTypeValue, isLightweightClass, isUnionOrIntersectionType} from '../easy-ast/ast-util';
|
||||
import {LightweightAliasDefinition} from '../easy-ast/types/lightweight-alias-definition';
|
||||
import {LightweightClassDefinition} from '../easy-ast/types/lightweight-class-definition';
|
||||
import {LightweightDefinition} from '../easy-ast/types/lightweight-definition';
|
||||
import {LightweightProperty} from '../easy-ast/types/lightweight-property';
|
||||
import {LightweightType} from '../easy-ast/types/lightweight-type';
|
||||
import {UMLConfig} from './uml-config';
|
||||
|
||||
/**
|
||||
@@ -28,7 +29,7 @@ import {UMLConfig} from './uml-config';
|
||||
* to valid PlantUML Code, which will then be encoded, converted by the plantuml server
|
||||
* and saved as a .svg file in directory, in which this method was called
|
||||
*
|
||||
* @param definitions all type definitons of the project
|
||||
* @param definitions all type definitions of the project
|
||||
* @param config contains information on how the PlantUML should be generated
|
||||
* @param plantUmlBaseURL Hostname of the PlantUML-Server
|
||||
*/
|
||||
@@ -37,49 +38,29 @@ export async function createDiagram(
|
||||
config: UMLConfig,
|
||||
plantUmlBaseURL: string,
|
||||
): Promise<string> {
|
||||
|
||||
// when non definitions were specified use all
|
||||
if (config.definitions.length === 0) {
|
||||
config.definitions = [];
|
||||
definitions.forEach((definition) => {
|
||||
config.definitions.push(definition.name);
|
||||
});
|
||||
}
|
||||
config.definitions = map(definitions, 'name');
|
||||
|
||||
// when providing definitions and either showing associations or inheritance the
|
||||
// inherited definitions will be added automatically
|
||||
if (config.showInheritance) {
|
||||
const inheritedDefinitions = gatherTypeAssociations(
|
||||
// TODO: showInheritance
|
||||
/*const inheritedDefinitions = gatherTypeAssociations(
|
||||
definitions,
|
||||
config.definitions,
|
||||
);
|
||||
|
||||
config.definitions = config.definitions.concat(inheritedDefinitions);
|
||||
);*/
|
||||
// config.definitions = config.definitions.concat(inheritedDefinitions);
|
||||
}
|
||||
|
||||
let modelPlantUMLCode = '';
|
||||
// creates a UML definition for every specified definition name
|
||||
// however if no definitions were provided all definitions will be transformed
|
||||
for (const definition of definitions) {
|
||||
if (
|
||||
config.definitions.length > 0 &&
|
||||
!config.definitions.includes(definition.name)
|
||||
) {
|
||||
// current definition not specified
|
||||
continue;
|
||||
}
|
||||
// either the definitions are empty or the definition was specified, proceed
|
||||
|
||||
let definitionPlantUMLCode = '';
|
||||
if (definition instanceof LightweightClassDefinition) {
|
||||
definitionPlantUMLCode = createPlantUMLCodeForClass(config, definition);
|
||||
} else if (definition instanceof LightweightEnumDefinition) {
|
||||
definitionPlantUMLCode = createPlantUMLCodeForEnum(config, definition);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
modelPlantUMLCode += definitionPlantUMLCode;
|
||||
}
|
||||
const modelPlantUMLCode = map(
|
||||
definitions.filter(it => !config.definitions.includes(it.name)),
|
||||
definition =>
|
||||
isLightweightClass(definition)
|
||||
? createPlantUMLCodeForClass(config, definition)
|
||||
: createPlantUMLCodeForEnum(config, definition),
|
||||
).join('');
|
||||
|
||||
return createDiagramFromString(modelPlantUMLCode, plantUmlBaseURL, config.outputFileName);
|
||||
}
|
||||
@@ -97,6 +78,7 @@ export async function createDiagramFromString(
|
||||
plantUmlBaseURL: string,
|
||||
outputFile = `Diagram-${new Date().toISOString()}`,
|
||||
) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires,unicorn/prefer-module
|
||||
const plantumlEncoder = require('plantuml-encoder');
|
||||
const plantUMLCode = plantumlEncoder.encode(`@startuml\n${modelPlantUMLCode}\n@enduml`);
|
||||
const url = `${plantUmlBaseURL}/svg/${plantUMLCode}`;
|
||||
@@ -108,17 +90,18 @@ export async function createDiagramFromString(
|
||||
await Logger.error(`Plantuml Server responded with an error.\n${response.statusMessage}`);
|
||||
throw new Error('Response not okay');
|
||||
}
|
||||
} catch (e) {
|
||||
Logger.log(`Please try using the public plantuml server:\nhttp://www.plantuml.com/plantuml/svg/${plantUMLCode}`);
|
||||
throw e;
|
||||
} catch (error) {
|
||||
Logger.log(
|
||||
`Please try using the public plantuml server:\nhttp://www.plantuml.com/plantuml/svg/${plantUMLCode}`,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
// attach file extension
|
||||
const fileName = `${outputFile}.svg`;
|
||||
try {
|
||||
createWriteStream(fileName)
|
||||
.write(response.body);
|
||||
createWriteStream(fileName).write(response.body);
|
||||
Logger.log(`Writen data to file: ${fileName}`);
|
||||
} catch (e) {
|
||||
} catch {
|
||||
throw new Error('Could not write file. Are you missing permissions?');
|
||||
}
|
||||
|
||||
@@ -126,12 +109,13 @@ export async function createDiagramFromString(
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursivly iterates over all types, to find implemented generic types and parents
|
||||
* Recursively iterates over all types, to find implemented generic types and parents
|
||||
*
|
||||
* @param definitions all type definitons of the project
|
||||
* @param definitions all type definitions of the project
|
||||
* @param abstractionNames currently known string values of inherited classes
|
||||
*/
|
||||
function gatherTypeAssociations(
|
||||
|
||||
/*function gatherTypeAssociations(
|
||||
definitions: LightweightDefinition[],
|
||||
abstractionNames: string[],
|
||||
): string[] {
|
||||
@@ -140,7 +124,7 @@ function gatherTypeAssociations(
|
||||
const declaration = definitions.find(
|
||||
(definition) => definition.name === name,
|
||||
);
|
||||
if (declaration instanceof LightweightClassDefinition) {
|
||||
if (isLightweightClass(declaration)) {
|
||||
const currentAbstractions: string[] = declaration.extendedDefinitions.concat(
|
||||
declaration.implementedDefinitions,
|
||||
);
|
||||
@@ -153,7 +137,7 @@ function gatherTypeAssociations(
|
||||
}
|
||||
|
||||
return abstractions;
|
||||
}
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Collects all reference information of this type.
|
||||
@@ -164,25 +148,22 @@ function gatherTypeAssociations(
|
||||
*/
|
||||
function getReferenceTypes(type: LightweightType): string[] {
|
||||
const types: string[] = [];
|
||||
if (type.isReference) {
|
||||
types.push(type.name);
|
||||
if (typeof type.referenceName !== 'undefined') {
|
||||
types.push(type.referenceName);
|
||||
}
|
||||
if (type.isTyped && type.genericsTypes.length > 0) {
|
||||
for (const specificType of type.genericsTypes) {
|
||||
|
||||
forEach(type.genericsTypes, specificType => {
|
||||
for (const value of getReferenceTypes(specificType)) {
|
||||
types.push(value);
|
||||
}
|
||||
});
|
||||
|
||||
if ((isUnionOrIntersectionType(type) && isEmpty(type.specificationTypes)) || type.isArray) {
|
||||
forEach(type.specificationTypes, specificType => {
|
||||
for (const value of getReferenceTypes(specificType)) {
|
||||
types.push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (
|
||||
(type.isUnion && type.specificationTypes.length > 0) ||
|
||||
(type.isArray && type.specificationTypes.length > 0)
|
||||
) {
|
||||
for (const specificType of type.specificationTypes) {
|
||||
for (const value of getReferenceTypes(specificType)) {
|
||||
types.push(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return types;
|
||||
@@ -194,57 +175,54 @@ function getReferenceTypes(type: LightweightType): string[] {
|
||||
* @param config Configuration for how the UML should be tweaked
|
||||
* @param readerClass Class or interface representation
|
||||
*/
|
||||
function createPlantUMLCodeForClass(
|
||||
config: UMLConfig,
|
||||
readerClass: LightweightClassDefinition,
|
||||
): string {
|
||||
function createPlantUMLCodeForClass(config: UMLConfig, readerClass: LightweightClassDefinition): string {
|
||||
// create the definition header, what type the definition is, it's name and it's inheritance
|
||||
let model = `${readerClass.type} ${readerClass.name}`;
|
||||
let model = `${readerClass.modifiers} ${readerClass.name}`;
|
||||
|
||||
if (readerClass.typeParameters.length > 0) {
|
||||
model += `<${readerClass.typeParameters.join(', ')}>`;
|
||||
if (readerClass.typeParameters?.length ?? 0 > 0) {
|
||||
model += `<${readerClass.typeParameters!.join(', ')}>`;
|
||||
}
|
||||
|
||||
if (config.showInheritance && readerClass.extendedDefinitions.length > 0) {
|
||||
if (config.showInheritance && (readerClass.extendedDefinitions?.length ?? 0 > 0)) {
|
||||
// PlantUML will automatically create links, when using extends
|
||||
model += ` extends ${readerClass.extendedDefinitions.join(', ')}`;
|
||||
model += ` extends ${readerClass.extendedDefinitions!.join(', ')}`;
|
||||
}
|
||||
if (config.showInheritance && readerClass.implementedDefinitions.length > 0) {
|
||||
// PlantUML will automatically create links, when using implenents
|
||||
model += ` implements ${readerClass.implementedDefinitions.join(', ')}`;
|
||||
if (config.showInheritance && (readerClass.implementedDefinitions?.length ?? 0 > 0)) {
|
||||
// PlantUML will automatically create links, when using implements
|
||||
model += ` implements ${readerClass.implementedDefinitions!.join(', ')}`;
|
||||
}
|
||||
model += '{';
|
||||
|
||||
// add the properties to the definition body
|
||||
if (config.showProperties) {
|
||||
for (const property of readerClass.properties) {
|
||||
forEach(readerClass.properties, property => {
|
||||
if (property.optional && !config.showOptionalProperties) {
|
||||
// don't show optional attributes
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
if (property.inherited && !config.showInheritedProperties) {
|
||||
/*if (property.inherited && !config.showInheritedProperties) {
|
||||
// don't show inherited properties
|
||||
continue;
|
||||
}
|
||||
}*/
|
||||
model += `\n\t${createPropertyLine(property)}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// close the definition body
|
||||
model += '\n}\n';
|
||||
|
||||
// add associations from properties with references
|
||||
for (const property of readerClass.properties) {
|
||||
forEach(readerClass.properties, property => {
|
||||
const types: string[] = getReferenceTypes(property.type);
|
||||
for (const type of types) {
|
||||
if ( config.showAssociations) {
|
||||
if (property.inherited && !config.showInheritedProperties) {
|
||||
if (config.showAssociations) {
|
||||
/*if (property.inherited && !config.showInheritedProperties) {
|
||||
continue;
|
||||
}
|
||||
}*/
|
||||
model += `${readerClass.name} -up-> ${type} : ${property.name} >\n`;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return model;
|
||||
}
|
||||
@@ -255,17 +233,14 @@ function createPlantUMLCodeForClass(
|
||||
* @param config Configuration for how the UML should be tweaked
|
||||
* @param readerEnum Enum/-like representation
|
||||
*/
|
||||
function createPlantUMLCodeForEnum(
|
||||
config: UMLConfig,
|
||||
readerEnum: LightweightEnumDefinition,
|
||||
): string {
|
||||
function createPlantUMLCodeForEnum(config: UMLConfig, readerEnum: LightweightAliasDefinition): string {
|
||||
// create enum header
|
||||
let model = `enum ${readerEnum.name} {`;
|
||||
// add values
|
||||
if (config.showEnumValues) {
|
||||
for (const value of readerEnum.values) {
|
||||
forEach(readerEnum.type?.specificationTypes, value => {
|
||||
model += `\n\t${value.toString()}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
model += '\n}\n';
|
||||
|
||||
@@ -276,7 +251,7 @@ function createPlantUMLCodeForEnum(
|
||||
* Creates a property PlantUML Line
|
||||
*/
|
||||
function createPropertyLine(property: LightweightProperty): string {
|
||||
const prefix = `${(property.inherited ? '/ ' : '')}${(property.optional ? '? ' : '')}`;
|
||||
const prefix = `${/*(property.inherited ? '/ ' : */ ''}${property.optional ? '? ' : ''}`;
|
||||
|
||||
return `${prefix}${property.name} : ${getFullTypeName(property.type)}`;
|
||||
return `${prefix}${property.name} : ${expandTypeValue(property.type)}`;
|
||||
}
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {LightweightDefinition} from './lightweight-definition';
|
||||
import {LightweightProperty} from './lightweight-property';
|
||||
/**
|
||||
* Represents a class definition
|
||||
*/
|
||||
export class LightweightClassDefinition extends LightweightDefinition {
|
||||
/**
|
||||
* String values of the extended definitions
|
||||
*/
|
||||
public extendedDefinitions: string[];
|
||||
|
||||
/**
|
||||
* String values of the implemented definitions
|
||||
*/
|
||||
public implementedDefinitions: string[];
|
||||
|
||||
/**
|
||||
* Properties of the definition
|
||||
*/
|
||||
public properties: LightweightProperty[];
|
||||
|
||||
/**
|
||||
* The definition type
|
||||
* e.g. `interface`/[`abstract`] `class`
|
||||
*/
|
||||
public type: string;
|
||||
|
||||
/**
|
||||
* Generic type parameters of this class
|
||||
*/
|
||||
public typeParameters: string[];
|
||||
|
||||
constructor(name: string, type: string) {
|
||||
super(name);
|
||||
this.type = type;
|
||||
this.properties = [];
|
||||
this.extendedDefinitions = [];
|
||||
this.implementedDefinitions = [];
|
||||
this.typeParameters = [];
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents any definition without specifics
|
||||
*/
|
||||
export abstract class LightweightDefinition {
|
||||
/**
|
||||
* Name of the definiton
|
||||
*/
|
||||
public name: string;
|
||||
|
||||
constructor(name: string) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {LightweightDefinition} from './lightweight-definition';
|
||||
/**
|
||||
* Represents an enum definition
|
||||
*/
|
||||
export class LightweightEnumDefinition extends LightweightDefinition {
|
||||
/**
|
||||
* Enumeration or union values
|
||||
*/
|
||||
public values: string[];
|
||||
|
||||
constructor(name: string) {
|
||||
super(name);
|
||||
this.values = [];
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {LightweightType} from './lightweight-type';
|
||||
|
||||
/**
|
||||
* Represents a property definition
|
||||
*/
|
||||
export class LightweightProperty {
|
||||
/**
|
||||
* Is the property inherited from another definition
|
||||
*/
|
||||
public inherited: boolean;
|
||||
|
||||
/**
|
||||
* Name of the property
|
||||
*/
|
||||
public name: string;
|
||||
|
||||
/**
|
||||
* Is the property marked as optional
|
||||
*/
|
||||
public optional: boolean;
|
||||
|
||||
/**
|
||||
* Type of the property
|
||||
*/
|
||||
public type: LightweightType;
|
||||
|
||||
/**
|
||||
* Constructor for LightweightProperty
|
||||
*
|
||||
* @param name Name of the property
|
||||
* @param type Type of the property
|
||||
* @param optional Is the property optional
|
||||
*/
|
||||
constructor(name: string, type: LightweightType, optional = true) {
|
||||
this.name = name;
|
||||
this.optional = optional;
|
||||
this.inherited = false;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Describes an easy to use type definition.
|
||||
*/
|
||||
export class LightweightType {
|
||||
/**
|
||||
* Contains all types inside of <> brackets
|
||||
*/
|
||||
genericsTypes: LightweightType[];
|
||||
|
||||
/**
|
||||
* Does the type have generic-parameters
|
||||
*/
|
||||
hasTypeInformation = false;
|
||||
|
||||
/**
|
||||
* Does the type represent an array type
|
||||
*/
|
||||
isArray = false;
|
||||
|
||||
/**
|
||||
* Does the type represent a literal type
|
||||
*/
|
||||
isLiteral = false;
|
||||
|
||||
/**
|
||||
* Does the type represent a primitive type
|
||||
*/
|
||||
isPrimitive = false;
|
||||
|
||||
/**
|
||||
* Does the type contain a reference to
|
||||
*/
|
||||
isReference = false;
|
||||
|
||||
/**
|
||||
* Is the type a reflection and not avaiblabe at compile time
|
||||
*/
|
||||
isReflection = false;
|
||||
|
||||
/**
|
||||
* Does the type have type parameters
|
||||
*/
|
||||
isTyped = false;
|
||||
|
||||
/**
|
||||
* Is the type a typed parameter
|
||||
*/
|
||||
isTypeParameter = false;
|
||||
|
||||
/**
|
||||
* Is the type a union type
|
||||
*/
|
||||
isUnion = false;
|
||||
|
||||
/**
|
||||
* Name of the type
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* Type specifications, if the type is combined by either an array, union or a typeOperator
|
||||
*/
|
||||
specificationTypes: LightweightType[];
|
||||
|
||||
constructor() {
|
||||
this.specificationTypes = [];
|
||||
this.genericsTypes = [];
|
||||
this.name = '';
|
||||
}
|
||||
}
|
||||
@@ -1,471 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {Logger} from '@openstapps/logger';
|
||||
import {
|
||||
ArrayType,
|
||||
ConditionalType,
|
||||
DeclarationReflection,
|
||||
IntrinsicType,
|
||||
ProjectReflection,
|
||||
QueryType,
|
||||
ReferenceType,
|
||||
ReflectionKind,
|
||||
ReflectionType,
|
||||
StringLiteralType,
|
||||
Type,
|
||||
TypeOperatorType,
|
||||
TypeParameterType,
|
||||
UnionType,
|
||||
} from 'typedoc/dist/lib/models';
|
||||
import {getFullTypeName} from '../common';
|
||||
import {LightweightClassDefinition} from './model/lightweight-class-definition';
|
||||
import {LightweightDefinition} from './model/lightweight-definition';
|
||||
import {LightweightEnumDefinition} from './model/lightweight-enum-definition';
|
||||
import {LightweightProperty} from './model/lightweight-property';
|
||||
import {LightweightType} from './model/lightweight-type';
|
||||
|
||||
/**
|
||||
* Reads the reflection model from typedoc and converts it into a flatter, easier to handle model
|
||||
*
|
||||
* @param srcPath Path to source file directory
|
||||
*/
|
||||
export function readDefinitions(projectReflection: ProjectReflection): LightweightDefinition[] {
|
||||
|
||||
const definitions: LightweightDefinition[] = [];
|
||||
|
||||
// define known types and categorize them
|
||||
const enumLike: string[] = ['Type alias', 'Enumeration'];
|
||||
const classLike: string[] = ['Class', 'Interface'];
|
||||
const unused: string[] = ['Function', 'Object literal', 'Variable'];
|
||||
|
||||
// children need to be not undefined, if they are return empty
|
||||
if (typeof projectReflection.children === 'undefined') {
|
||||
return [];
|
||||
}
|
||||
|
||||
for (const module of projectReflection.children) {
|
||||
if (Array.isArray(module.children) && module.children.length > 0) {
|
||||
// iterate over class and enum declarations
|
||||
for (const type of module.children) {
|
||||
// only if kindString is set
|
||||
if (typeof type.kindString !== 'undefined') {
|
||||
// check if declaration is enum
|
||||
if (classLike.includes(type.kindString)) {
|
||||
definitions.push(readAsClassDefinition(type));
|
||||
} else if (enumLike.includes(type.kindString)) {
|
||||
definitions.push(readAsEnumDefinition(type));
|
||||
} else if (unused.includes(type.kindString)) {
|
||||
Logger.info(`Unconverted ${type.kindString} : ${type.name}`);
|
||||
} else {
|
||||
Logger.log(
|
||||
`Uncaught declaration type (${type.kindString}) : ${type.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return definitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the declaration into a `LightweightClassDefinition`
|
||||
*
|
||||
* @param declaration declaration
|
||||
*/
|
||||
export function readAsEnumDefinition(
|
||||
declaration: DeclarationReflection,
|
||||
): LightweightEnumDefinition {
|
||||
// init enum definition
|
||||
const enumDefinition: LightweightEnumDefinition = new LightweightEnumDefinition(
|
||||
declaration.name,
|
||||
);
|
||||
|
||||
// get enum values according to type
|
||||
if (declaration.kindString === 'Enumeration' && typeof declaration.children !== 'undefined') {
|
||||
// standard enumeration
|
||||
for (const child of declaration.children) {
|
||||
if (child.kindString === 'Enumeration member') {
|
||||
let value = child.name;
|
||||
if (typeof child.defaultValue !== 'undefined') {
|
||||
value = `${value} = ${child.defaultValue}`;
|
||||
}
|
||||
enumDefinition.values.push(value);
|
||||
} else {
|
||||
Logger.log(
|
||||
"Every enumeration member should be an 'EnumerationMemberType'",
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
declaration.kindString === 'Type alias' &&
|
||||
typeof declaration.type !== 'undefined'
|
||||
) {
|
||||
// enum like declaration
|
||||
try {
|
||||
const a = readTypeInformation(declaration.type);
|
||||
enumDefinition.values = enumDefinition.values.concat(
|
||||
getTypeInformation(a),
|
||||
);
|
||||
} catch (e) {
|
||||
Logger.warn(
|
||||
`Could not read the light type for ${declaration.name}. ${e}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return enumDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for enumrations to get the type value
|
||||
*/
|
||||
function getTypeInformation(type: LightweightType): string[] {
|
||||
const values: string[] = [];
|
||||
if (!type.hasTypeInformation) {
|
||||
for (const specificType of type.specificationTypes) {
|
||||
for (const value of getTypeInformation(specificType)) {
|
||||
values.push(value);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
values.push(type.name);
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the declaration into a `LightweightClassDefinition`
|
||||
*
|
||||
* @param declaration declaration
|
||||
*/
|
||||
export function readAsClassDefinition(
|
||||
declaration: DeclarationReflection,
|
||||
): LightweightClassDefinition {
|
||||
let type = typeof declaration.kindString !== 'undefined' ? declaration.kindString.toLowerCase() : '';
|
||||
type = (declaration.flags.isAbstract ? 'abstract ' : '') + type;
|
||||
|
||||
const classDefinition: LightweightClassDefinition = new LightweightClassDefinition(
|
||||
declaration.name,
|
||||
type,
|
||||
);
|
||||
|
||||
// get generic types
|
||||
if (typeof declaration.typeParameters !== 'undefined') {
|
||||
const typeParameters: string[] = [];
|
||||
declaration.typeParameters.forEach((typeParameter) =>
|
||||
typeParameters.push(typeParameter.name),
|
||||
);
|
||||
classDefinition.typeParameters = typeParameters;
|
||||
}
|
||||
|
||||
// extracts extended types of the declaration
|
||||
if (typeof declaration.extendedTypes !== 'undefined') {
|
||||
for (const extType of declaration.extendedTypes) {
|
||||
classDefinition.extendedDefinitions.push((extType as ReferenceType).name);
|
||||
}
|
||||
}
|
||||
|
||||
// extracts implemented types of the declaration
|
||||
// HINT: typedoc automatically adds inherited interfaces to the declaration directly
|
||||
if (typeof declaration.implementedTypes !== 'undefined') {
|
||||
for (const implType of declaration.implementedTypes) {
|
||||
classDefinition.implementedDefinitions.push(
|
||||
(implType as ReferenceType).name,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof declaration.children !== 'undefined') {
|
||||
for (const child of declaration.getChildrenByKind(
|
||||
ReflectionKind.Property,
|
||||
)) {
|
||||
try {
|
||||
if (typeof child.type === 'undefined') {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
const myType: LightweightType = readTypeInformation(child.type);
|
||||
const property = new LightweightProperty(child.name, myType);
|
||||
|
||||
const flags = child.flags;
|
||||
if (flags.isOptional !== undefined) {
|
||||
property.optional = flags.isOptional as boolean;
|
||||
property.inherited = !(
|
||||
child.inheritedFrom === undefined || child.inheritedFrom === null
|
||||
);
|
||||
}
|
||||
classDefinition.properties.push(property);
|
||||
} catch (e) {
|
||||
Logger.warn(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return classDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* The structure of reflection type has a huge overhead
|
||||
* This method and all submethods will convert these types in easier to process Types
|
||||
*
|
||||
* @param declarationType Type to be converted
|
||||
*/
|
||||
function readTypeInformation(declarationType: Type): LightweightType {
|
||||
if (declarationType instanceof ReflectionType) {
|
||||
return readAsReflectionType(declarationType);
|
||||
}
|
||||
if (declarationType instanceof TypeOperatorType) {
|
||||
return readAsTypeOperatorType(declarationType);
|
||||
}
|
||||
if (declarationType instanceof TypeParameterType) {
|
||||
return readAsTypeParameterType(declarationType);
|
||||
}
|
||||
if (declarationType instanceof IntrinsicType) {
|
||||
return readAsIntrinsicType(declarationType);
|
||||
}
|
||||
if (declarationType instanceof StringLiteralType) {
|
||||
return readAsStringLiteralType(declarationType);
|
||||
}
|
||||
if (declarationType instanceof ReferenceType) {
|
||||
return readAsReferenceType(declarationType);
|
||||
}
|
||||
if (declarationType instanceof ArrayType) {
|
||||
return readAsArrayType(declarationType);
|
||||
}
|
||||
if (declarationType instanceof UnionType) {
|
||||
return readAsUnionType(declarationType);
|
||||
}
|
||||
if (declarationType instanceof QueryType) {
|
||||
return readAsQueryType(declarationType);
|
||||
}
|
||||
if (declarationType instanceof ConditionalType) {
|
||||
return readAsConditionalType(declarationType);
|
||||
}
|
||||
|
||||
throw new Error(`Could not read type ${declarationType.type}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Conversion method for ConditionalTypes
|
||||
*
|
||||
* @param _type Type to be converted
|
||||
*/
|
||||
function readAsConditionalType(_type: ConditionalType): LightweightType {
|
||||
const returnType: LightweightType = new LightweightType();
|
||||
|
||||
returnType.specificationTypes = [];
|
||||
returnType.name = getFullTypeName(returnType);
|
||||
returnType.isUnion = true;
|
||||
|
||||
return returnType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Conversion method for QueryTypes
|
||||
*
|
||||
* @param type Type to be converted
|
||||
*/
|
||||
function readAsQueryType(type: QueryType): LightweightType {
|
||||
const out = readAsReferenceType(type.queryType);
|
||||
out.isReference = true;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Conversion method for IntrinsicType's
|
||||
*
|
||||
* e.g. remainingAttendeeCapacity?: number;
|
||||
*
|
||||
* @param type Type to be converted
|
||||
*/
|
||||
function readAsIntrinsicType(type: IntrinsicType): LightweightType {
|
||||
const easyType: LightweightType = new LightweightType();
|
||||
easyType.name = type.name;
|
||||
easyType.isPrimitive = true;
|
||||
easyType.hasTypeInformation = true;
|
||||
|
||||
return easyType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Conversion method for StringLiteralType's
|
||||
*
|
||||
* e.g. inputType: 'multipleChoice';
|
||||
*
|
||||
* @param type Type to be converted
|
||||
*/
|
||||
function readAsStringLiteralType(type: StringLiteralType): LightweightType {
|
||||
const returnType: LightweightType = new LightweightType();
|
||||
returnType.name = type.value;
|
||||
returnType.isLiteral = true;
|
||||
returnType.hasTypeInformation = true;
|
||||
|
||||
return returnType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Conversion method for ReferenceType's
|
||||
*
|
||||
* Everything that is a user or API designed definition and not a primitive type or core-language feature.
|
||||
*
|
||||
* e.g. publishers?: Array<SCPersonWithoutReferences | SCOrganizationWithoutReferences>;
|
||||
*
|
||||
* Array, SCPersonWithoutReferences and SCOrganizationWithoutReferences will be recognized as reference types!
|
||||
*
|
||||
* @param type Type to be converted
|
||||
*/
|
||||
function readAsReferenceType(type: ReferenceType): LightweightType {
|
||||
const returnType: LightweightType = new LightweightType();
|
||||
returnType.name = type.name;
|
||||
|
||||
if (type.typeArguments !== undefined && type.typeArguments.length > 0) {
|
||||
const typeArguments: LightweightType[] = [];
|
||||
|
||||
for (const value of type.typeArguments) {
|
||||
typeArguments.push(readTypeInformation(value));
|
||||
}
|
||||
|
||||
returnType.isTyped = true;
|
||||
returnType.genericsTypes = typeArguments;
|
||||
}
|
||||
|
||||
if (type.reflection !== undefined && type.reflection !== null) {
|
||||
const tempTypeReflection = type.reflection as DeclarationReflection;
|
||||
// interfaces and classes in a type are a sink, since their declaration are defined elsewhere
|
||||
if (
|
||||
typeof tempTypeReflection.kindString !== 'undefined' &&
|
||||
['Interface', 'Class', 'Enumeration', 'Type alias'].includes(
|
||||
tempTypeReflection.kindString)) {
|
||||
returnType.isReference = true;
|
||||
}
|
||||
}
|
||||
|
||||
returnType.hasTypeInformation = true;
|
||||
|
||||
return returnType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Conversion method for ArrayType's
|
||||
*
|
||||
* The actual type of the array is stored in the first element of specificationTypes.
|
||||
*
|
||||
* e.g. articleBody?: string[];
|
||||
*
|
||||
* @param type Type to be converted
|
||||
*/
|
||||
function readAsArrayType(type: ArrayType): LightweightType {
|
||||
const returnType: LightweightType = new LightweightType();
|
||||
const typeOfArray: LightweightType = readTypeInformation(type.elementType);
|
||||
returnType.name = getFullTypeName(typeOfArray);
|
||||
returnType.specificationTypes = [typeOfArray];
|
||||
returnType.isArray = true;
|
||||
|
||||
return returnType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Conversion method for UnionType's
|
||||
*
|
||||
* The Union-LightType store the single types of the union inside a
|
||||
* separate LightType inside specificationTypes.
|
||||
*
|
||||
* e.g. maintainer?: SCPerson | SCOrganization;
|
||||
*
|
||||
* @param type Type to be converted
|
||||
*/
|
||||
function readAsUnionType(type: UnionType): LightweightType {
|
||||
const returnType: LightweightType = new LightweightType();
|
||||
const typesOfUnion: LightweightType[] = [];
|
||||
for (const value of type.types) {
|
||||
typesOfUnion.push(readTypeInformation(value));
|
||||
}
|
||||
returnType.specificationTypes = typesOfUnion;
|
||||
returnType.name = getFullTypeName(returnType);
|
||||
returnType.isUnion = true;
|
||||
|
||||
return returnType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Conversion method for ReflectionType's
|
||||
*
|
||||
* The explicit type is not contained in reflection!
|
||||
* It might be possible to get the structure of type by reading tempType.decoration.children,
|
||||
* but this structure is currently not supported in the data-model.
|
||||
*
|
||||
* e.g. categorySpecificValues?: { [s: string]: U };
|
||||
*
|
||||
* @param type Type to be converted
|
||||
*/
|
||||
function readAsReflectionType(type: ReflectionType): LightweightType {
|
||||
const returnType: LightweightType = new LightweightType();
|
||||
if (typeof type.declaration.sources !== 'undefined') {
|
||||
const src = type.declaration.sources[0];
|
||||
Logger.warn(
|
||||
`${src.line} : ${src.fileName}: Reflection Type not recognized. Refactoring to explicit class is advised.`,
|
||||
);
|
||||
}
|
||||
returnType.name = 'object';
|
||||
returnType.isReflection = true;
|
||||
|
||||
return returnType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Conversion method for TypeOperatorType's
|
||||
*
|
||||
* This type is similar to reflection, that the actual type can only be evaluated at runtime.
|
||||
*
|
||||
* e.g. universityRole: keyof SCSportCoursePriceGroup;
|
||||
*
|
||||
* @param type Type to be converted
|
||||
*/
|
||||
function readAsTypeOperatorType(type: TypeOperatorType): LightweightType {
|
||||
const returnType: LightweightType = new LightweightType();
|
||||
const typeOf: LightweightType = readTypeInformation(type.target);
|
||||
returnType.name = `keyof ${getFullTypeName(typeOf)}`;
|
||||
returnType.specificationTypes = [typeOf];
|
||||
// can't be traced deeper! so might as well be a primitive
|
||||
returnType.isPrimitive = true;
|
||||
returnType.hasTypeInformation = true;
|
||||
|
||||
return returnType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Conversion method for TypeParameterType's
|
||||
*
|
||||
* Should only be called in generic classes/interfaces, when a property is
|
||||
* referencing the generic-type.
|
||||
*
|
||||
* e.g. prices?: T;
|
||||
*
|
||||
* Does not match on Arrays of the generic type. Those will be matched with ArrayType.
|
||||
*
|
||||
* @param type Needs to be a TypeParameterType
|
||||
*/
|
||||
function readAsTypeParameterType(type: TypeParameterType): LightweightType {
|
||||
const returnType: LightweightType = new LightweightType();
|
||||
returnType.name = type.name;
|
||||
returnType.isTypeParameter = true;
|
||||
returnType.hasTypeInformation = true;
|
||||
|
||||
return returnType;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2019 StApps
|
||||
* Copyright (C) 2021 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
Reference in New Issue
Block a user