mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-21 00:52:55 +00:00
Upgraded JSON Schema from version 6 to version 7 Upgraded TypeDoc version to latest Replaced 'jsonschema' with 'json-schema' package to better comply with 'ts-json-schema-generator' Replace JSON Schema validation with AJV in areas where it wasn't used previously Removed commander help output as it causes strange issues
472 lines
14 KiB
TypeScript
472 lines
14 KiB
TypeScript
/*
|
|
* 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;
|
|
}
|