mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-21 09:03:02 +00:00
feat: modernize core-tools
This commit is contained in:
143
src/easy-ast/ast-internal-util.ts
Normal file
143
src/easy-ast/ast-internal-util.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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 {first, last, tail, filter} from 'lodash';
|
||||
import {
|
||||
ArrayTypeNode,
|
||||
ClassDeclaration,
|
||||
ClassElement,
|
||||
EnumDeclaration,
|
||||
Identifier,
|
||||
InterfaceDeclaration,
|
||||
isArrayTypeNode,
|
||||
isClassDeclaration,
|
||||
isComputedPropertyName,
|
||||
isEnumDeclaration,
|
||||
isInterfaceDeclaration,
|
||||
isPropertyDeclaration,
|
||||
isPropertySignature,
|
||||
isTypeAliasDeclaration,
|
||||
isTypeReferenceNode,
|
||||
NodeArray,
|
||||
PropertyDeclaration,
|
||||
PropertyName,
|
||||
PropertySignature,
|
||||
TypeAliasDeclaration,
|
||||
TypeElement,
|
||||
TypeNode,
|
||||
TypeReferenceNode,
|
||||
} from 'typescript';
|
||||
import * as ts from 'typescript';
|
||||
import {cleanupEmpty} from '../util/collections';
|
||||
import {LightweightComment} from './types/lightweight-comment';
|
||||
|
||||
/** @internal */
|
||||
export function extractComment(node: ts.Node): LightweightComment | undefined {
|
||||
const jsDocument = last(
|
||||
// @ts-expect-error jsDoc exists in reality
|
||||
node.jsDoc as
|
||||
| Array<{
|
||||
comment?: string;
|
||||
tags?: Array<{comment?: string; tagName?: {escapedText?: string}}>;
|
||||
}>
|
||||
| undefined,
|
||||
);
|
||||
const comment = jsDocument?.comment?.split('\n\n');
|
||||
|
||||
return typeof jsDocument === 'undefined'
|
||||
? undefined
|
||||
: cleanupEmpty({
|
||||
shortSummary: first(comment),
|
||||
description: tail(comment)?.join('\n\n'),
|
||||
tags: jsDocument?.tags?.map(tag =>
|
||||
cleanupEmpty({
|
||||
name: tag.tagName?.escapedText ?? 'UNRESOLVED_NAME',
|
||||
parameters: tag.comment?.split(' '),
|
||||
}),
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function isProperty(
|
||||
node: ClassElement | TypeElement,
|
||||
): node is PropertyDeclaration | PropertySignature {
|
||||
return isPropertyDeclaration(node) || isPropertySignature(node);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function filterNodeTo<T extends ts.Node, S extends T>(
|
||||
node: NodeArray<T>,
|
||||
check: (node: T) => node is S,
|
||||
): S[] {
|
||||
return filter(node, check);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function filterChildrenTo<T extends ts.Node>(node: ts.Node, check: (node: ts.Node) => node is T): T[] {
|
||||
const out: T[] = [];
|
||||
node.forEachChild(child => {
|
||||
if (check(child)) {
|
||||
out.push(child);
|
||||
}
|
||||
});
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function getModifiers(text: string, kind: string): string[] {
|
||||
return [
|
||||
...text
|
||||
.split(kind)[0]
|
||||
.split(/\s+/)
|
||||
.filter(it => it !== ''),
|
||||
kind,
|
||||
];
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function resolvePropertyName(name?: PropertyName): string | undefined {
|
||||
return typeof name !== 'undefined'
|
||||
? isComputedPropertyName(name)
|
||||
? 'UNSUPPORTED_IDENTIFIER_TYPE'
|
||||
: name.getText()
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function resolveTypeName(type?: TypeNode): string | undefined {
|
||||
// @ts-expect-error typeName exists in reality
|
||||
return type?.typeName?.escapedText ?? type?.typeName?.right?.escapedText;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function isArrayLikeType(typeNode?: TypeNode): typeNode is ArrayTypeNode | TypeReferenceNode {
|
||||
return typeof typeNode !== 'undefined' && (isArrayTypeNode(typeNode) || isArrayReference(typeNode));
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function isArrayReference(typeNode: TypeNode): boolean {
|
||||
return isTypeReferenceNode(typeNode) && (typeNode.typeName as Identifier).escapedText === 'Array';
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function isClassLikeNode(node: ts.Node): node is ClassDeclaration | InterfaceDeclaration {
|
||||
return isClassDeclaration(node) || isInterfaceDeclaration(node);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function isEnumLikeNode(node: ts.Node): node is EnumDeclaration | TypeAliasDeclaration {
|
||||
return isEnumDeclaration(node) || isTypeAliasDeclaration(node);
|
||||
}
|
||||
83
src/easy-ast/ast-util.ts
Normal file
83
src/easy-ast/ast-util.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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 {flatMap, keyBy, isEmpty} from 'lodash';
|
||||
import {TypeFlags} from 'typescript';
|
||||
import {LightweightAliasDefinition} from './types/lightweight-alias-definition';
|
||||
import {LightweightClassDefinition} from './types/lightweight-class-definition';
|
||||
import {LightweightDefinition} from './types/lightweight-definition';
|
||||
import {LightweightDefinitionKind} from './types/lightweight-definition-kind';
|
||||
import {LightweightProject} from './types/lightweight-project';
|
||||
import {LightweightType} from './types/lightweight-type';
|
||||
|
||||
/**
|
||||
* Creates a printable name of a type
|
||||
*/
|
||||
export function expandTypeValue(type: LightweightType): string | undefined {
|
||||
if (type.isArray) {
|
||||
return `${type.value}[]`;
|
||||
}
|
||||
if (isStringLiteralType(type)) {
|
||||
return `'${type.value}'`;
|
||||
}
|
||||
if (isUnionOrIntersectionType(type)) {
|
||||
return type.specificationTypes?.map(expandTypeValue).join(isUnionType(type) ? ' | ' : ' & ');
|
||||
}
|
||||
if (isEmpty(type.genericsTypes)) {
|
||||
return `${type.value}<${type.genericsTypes?.map(expandTypeValue).join(', ')}>`;
|
||||
}
|
||||
|
||||
return type.value?.toString();
|
||||
}
|
||||
|
||||
export function definitionsOf(project: LightweightProject): Record<string, LightweightDefinition> {
|
||||
return keyBy(flatMap(project, Object.values), 'name');
|
||||
}
|
||||
|
||||
export function isPrimitiveType(type: {flags: TypeFlags}): boolean {
|
||||
return (type.flags & TypeFlags.NonPrimitive) === 0;
|
||||
}
|
||||
|
||||
export function isLiteralType(type: {flags: TypeFlags}): boolean {
|
||||
return (type.flags & TypeFlags.Literal) !== 0;
|
||||
}
|
||||
|
||||
export function isEnumLiteralType(type: {flags: TypeFlags}): boolean {
|
||||
return (type.flags & TypeFlags.EnumLiteral) !== 0;
|
||||
}
|
||||
|
||||
export function isStringLiteralType(type: {flags: TypeFlags}): boolean {
|
||||
return (type.flags & TypeFlags.StringLiteral) !== 0;
|
||||
}
|
||||
|
||||
export function isUnionOrIntersectionType(type: {flags: TypeFlags}): boolean {
|
||||
return (type.flags & TypeFlags.UnionOrIntersection) !== 0;
|
||||
}
|
||||
|
||||
export function isUnionType(type: {flags: TypeFlags}): boolean {
|
||||
return (type.flags & TypeFlags.Union) !== 0;
|
||||
}
|
||||
|
||||
export function isLightweightClass(node?: LightweightDefinition): node is LightweightClassDefinition {
|
||||
return node?.kind === LightweightDefinitionKind.CLASS_LIKE;
|
||||
}
|
||||
|
||||
export function isLightweightEnum(node?: LightweightDefinition): node is LightweightAliasDefinition {
|
||||
return node?.kind === LightweightDefinitionKind.ALIAS_LIKE;
|
||||
}
|
||||
|
||||
export function isTypeVariable(type: {flags: TypeFlags}): boolean {
|
||||
return (type.flags & TypeFlags.TypeVariable) !== 0;
|
||||
}
|
||||
274
src/easy-ast/easy-ast.ts
Normal file
274
src/easy-ast/easy-ast.ts
Normal file
@@ -0,0 +1,274 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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 {flatMap, groupBy, keyBy, mapValues} from 'lodash';
|
||||
import * as ts from 'typescript';
|
||||
import {
|
||||
ClassDeclaration,
|
||||
ClassElement,
|
||||
EnumDeclaration,
|
||||
InterfaceDeclaration,
|
||||
isArrayTypeNode,
|
||||
isClassDeclaration,
|
||||
isEnumDeclaration,
|
||||
isIndexSignatureDeclaration,
|
||||
isPropertyDeclaration,
|
||||
isTypeLiteralNode,
|
||||
isTypeReferenceNode,
|
||||
NodeArray,
|
||||
Program,
|
||||
SourceFile,
|
||||
SyntaxKind,
|
||||
Type,
|
||||
TypeAliasDeclaration,
|
||||
TypeChecker,
|
||||
TypeElement,
|
||||
TypeFlags,
|
||||
TypeLiteralNode,
|
||||
TypeNode,
|
||||
} from 'typescript';
|
||||
import {cleanupEmpty, mapNotNil, rejectNil} from '../util/collections';
|
||||
import {expandPathToFilesSync} from '../util/io';
|
||||
import {
|
||||
extractComment,
|
||||
filterChildrenTo,
|
||||
filterNodeTo,
|
||||
getModifiers,
|
||||
isArrayLikeType,
|
||||
isClassLikeNode,
|
||||
isEnumLikeNode,
|
||||
isProperty,
|
||||
resolvePropertyName,
|
||||
resolveTypeName,
|
||||
} from './ast-internal-util';
|
||||
import {isEnumLiteralType, isTypeVariable} from './ast-util';
|
||||
import {LightweightAliasDefinition} from './types/lightweight-alias-definition';
|
||||
import {LightweightClassDefinition} from './types/lightweight-class-definition';
|
||||
import {LightweightDefinition} from './types/lightweight-definition';
|
||||
import {LightweightDefinitionKind} from './types/lightweight-definition-kind';
|
||||
import {LightweightProject} from './types/lightweight-project';
|
||||
import {LightweightType} from './types/lightweight-type';
|
||||
import path from 'path';
|
||||
import {LightweightProperty} from './types/lightweight-property';
|
||||
|
||||
/**
|
||||
* Convert a TypeScript project to a lightweight Type-AST representation of the project
|
||||
*
|
||||
* @param sourcePath either a directory or a set of input files
|
||||
* @param includeComments if comments should be included (default true)
|
||||
*/
|
||||
export function lightweightProjectFromPath(
|
||||
sourcePath: string | string[],
|
||||
includeComments = true,
|
||||
): LightweightProject {
|
||||
return new LightweightDefinitionBuilder(sourcePath, includeComments).convert();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a TypeScript project to a set of lightweight definition ASTs
|
||||
*
|
||||
* @param sourcePath either a directory or a set of input files
|
||||
* @param includeComments if comments should be included (default true)
|
||||
*/
|
||||
export function lightweightDefinitionsFromPath(
|
||||
sourcePath: string | string[],
|
||||
includeComments = true,
|
||||
): LightweightDefinition[] {
|
||||
return rejectNil(new LightweightDefinitionBuilder(sourcePath, includeComments).convertToList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the reflection model and converts it into a flatter, easier to handle model
|
||||
*/
|
||||
class LightweightDefinitionBuilder {
|
||||
readonly program: Program;
|
||||
|
||||
readonly sourceFiles: readonly SourceFile[];
|
||||
|
||||
readonly typeChecker: TypeChecker;
|
||||
|
||||
constructor(sourcePath: string | string[], readonly includeComments: boolean) {
|
||||
const rootNames = Array.isArray(sourcePath)
|
||||
? sourcePath
|
||||
: expandPathToFilesSync(path.resolve(sourcePath), file => file.endsWith('ts'));
|
||||
|
||||
this.program = ts.createProgram({
|
||||
rootNames: rootNames,
|
||||
options: {
|
||||
alwaysStrict: true,
|
||||
charset: 'utf8',
|
||||
declaration: true,
|
||||
esModuleInterop: true,
|
||||
experimentalDecorators: true,
|
||||
inlineSourceMap: true,
|
||||
module: ts.ModuleKind.CommonJS,
|
||||
strict: true,
|
||||
target: ts.ScriptTarget.ES2015,
|
||||
},
|
||||
});
|
||||
|
||||
this.typeChecker = this.program.getTypeChecker();
|
||||
this.sourceFiles = mapNotNil(this.program.getRootFileNames(), it => this.program.getSourceFile(it));
|
||||
}
|
||||
|
||||
private convertAliasLike(enumLike: EnumDeclaration | TypeAliasDeclaration): LightweightAliasDefinition {
|
||||
return cleanupEmpty({
|
||||
comment: this.includeComments ? extractComment(enumLike) : undefined,
|
||||
name: enumLike.name.getText() ?? 'ERROR',
|
||||
kind: LightweightDefinitionKind.ALIAS_LIKE,
|
||||
modifiers: getModifiers(enumLike.getText(), isEnumDeclaration(enumLike) ? 'enum' : 'type'),
|
||||
type: isEnumDeclaration(enumLike)
|
||||
? enumLike.members.length > 0
|
||||
? {
|
||||
flags: 1_048_576,
|
||||
specificationTypes: enumLike.members.map(it => this.lightweightTypeAtNode(it)),
|
||||
}
|
||||
: undefined
|
||||
: this.lightweightTypeFromType(this.typeChecker.getTypeFromTypeNode(enumLike.type), enumLike.type),
|
||||
});
|
||||
}
|
||||
|
||||
private convertClassLike(classLike: ClassDeclaration | InterfaceDeclaration): LightweightClassDefinition {
|
||||
const heritages = mapValues(
|
||||
groupBy(classLike.heritageClauses, it => it.token),
|
||||
heritages => flatMap(heritages, it => it.types),
|
||||
);
|
||||
|
||||
return cleanupEmpty({
|
||||
comment: this.includeComments ? extractComment(classLike) : undefined,
|
||||
name: classLike.name?.escapedText ?? 'ERROR',
|
||||
kind: LightweightDefinitionKind.CLASS_LIKE,
|
||||
modifiers: getModifiers(classLike.getText(), isClassDeclaration(classLike) ? 'class' : 'interface'),
|
||||
extendedDefinitions: heritages[ts.SyntaxKind.ExtendsKeyword]?.map(it => this.lightweightTypeAtNode(it)),
|
||||
implementedDefinitions: heritages[ts.SyntaxKind.ImplementsKeyword]?.map(it =>
|
||||
this.lightweightTypeAtNode(it),
|
||||
),
|
||||
indexSignatures: keyBy(
|
||||
filterNodeTo(
|
||||
classLike.members as NodeArray<ClassElement | TypeElement>,
|
||||
isIndexSignatureDeclaration,
|
||||
).map(indexSignature =>
|
||||
cleanupEmpty({
|
||||
name:
|
||||
this.typeChecker.getSignatureFromDeclaration(indexSignature)?.parameters?.[0]?.escapedName ??
|
||||
'UNRESOLVED_INDEX_SIGNATURE',
|
||||
type: this.lightweightTypeFromType(
|
||||
this.typeChecker.getTypeFromTypeNode(indexSignature.type),
|
||||
indexSignature.type,
|
||||
),
|
||||
indexSignatureType: this.lightweightTypeFromType(
|
||||
this.typeChecker.getTypeFromTypeNode(indexSignature.parameters[0].type!),
|
||||
indexSignature.parameters[0].type!,
|
||||
),
|
||||
}),
|
||||
),
|
||||
it => it.name,
|
||||
),
|
||||
typeParameters: classLike.typeParameters?.map(it => it.name.getText()),
|
||||
properties: this.collectProperties(classLike.members),
|
||||
});
|
||||
}
|
||||
|
||||
collectProperties(members: NodeArray<ClassElement | TypeElement>): Record<string, LightweightProperty> {
|
||||
return keyBy(
|
||||
filterNodeTo(members as NodeArray<ClassElement | TypeElement>, isProperty).map(property =>
|
||||
cleanupEmpty({
|
||||
comment: this.includeComments ? extractComment(property) : undefined,
|
||||
name: resolvePropertyName(property.name) ?? property.getText(),
|
||||
type: this.lightweightTypeAtNode(property),
|
||||
properties: this.collectProperties((property.type as TypeLiteralNode)?.members),
|
||||
optional: isPropertyDeclaration(property)
|
||||
? typeof property.questionToken !== 'undefined'
|
||||
? true
|
||||
: undefined
|
||||
: undefined,
|
||||
}),
|
||||
),
|
||||
it => it.name,
|
||||
);
|
||||
}
|
||||
|
||||
private lightweightTypeAtNode(node: ts.Node): LightweightType {
|
||||
const type = this.typeChecker.getTypeAtLocation(node);
|
||||
|
||||
return this.lightweightTypeFromType(type, this.typeChecker.typeToTypeNode(type, node, undefined));
|
||||
}
|
||||
|
||||
private lightweightTypeFromType(type: ts.Type, typeNode?: TypeNode): LightweightType {
|
||||
if (typeNode?.kind === SyntaxKind.ConditionalType) {
|
||||
return {value: 'UNSUPPORTED_CONDITIONAL_TYPE', flags: TypeFlags.Unknown};
|
||||
}
|
||||
if (isArrayLikeType(typeNode)) {
|
||||
const elementType = isArrayTypeNode(typeNode) ? typeNode.elementType : typeNode.typeArguments?.[0]!;
|
||||
const out = this.lightweightTypeFromType(
|
||||
this.typeChecker.getTypeFromTypeNode(elementType),
|
||||
elementType,
|
||||
);
|
||||
out.isArray = true;
|
||||
|
||||
return out;
|
||||
}
|
||||
const isReference =
|
||||
typeof typeNode !== 'undefined' && isTypeReferenceNode(typeNode) && !isEnumLiteralType(type);
|
||||
const isTypeLiteral = typeof typeNode !== 'undefined' && isTypeLiteralNode(typeNode);
|
||||
// @ts-expect-error intrinsic name & value exist
|
||||
const intrinsicName = (type.intrinsicName ?? type.value) as string | undefined;
|
||||
|
||||
return cleanupEmpty({
|
||||
value: intrinsicName,
|
||||
referenceName: isTypeLiteral
|
||||
? undefined
|
||||
: resolveTypeName(typeNode) ?? (type.symbol?.escapedName as string | undefined),
|
||||
flags: type.flags,
|
||||
genericsTypes: isTypeVariable(type)
|
||||
? undefined
|
||||
: this.typeChecker
|
||||
.getApparentType(type)
|
||||
// @ts-expect-error resolvedTypeArguments exits
|
||||
?.resolvedTypeArguments?.filter(it => !it.isThisType)
|
||||
?.map((it: Type) => this.lightweightTypeFromType(it)),
|
||||
specificationTypes:
|
||||
type.isUnionOrIntersection() && !isReference
|
||||
? type.types.map(it =>
|
||||
this.lightweightTypeFromType(it, this.typeChecker.typeToTypeNode(it, undefined, undefined)),
|
||||
)
|
||||
: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the conversion process
|
||||
*/
|
||||
convert(): LightweightProject {
|
||||
return mapValues(
|
||||
keyBy(this.sourceFiles, it => it.fileName),
|
||||
file =>
|
||||
keyBy(
|
||||
[
|
||||
...filterChildrenTo(file, isClassLikeNode).map(it => this.convertClassLike(it)),
|
||||
...filterChildrenTo(file, isEnumLikeNode).map(it => this.convertAliasLike(it)),
|
||||
],
|
||||
it => it.name,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as conversion, but generates a simple list of all definitions.
|
||||
*/
|
||||
convertToList(): LightweightDefinition[] {
|
||||
return flatMap(this.convert(), it => it.values);
|
||||
}
|
||||
}
|
||||
32
src/easy-ast/types/lightweight-alias-definition.d.ts
vendored
Normal file
32
src/easy-ast/types/lightweight-alias-definition.d.ts
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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 {LightweightDefinitionBase} from './lightweight-definition';
|
||||
import {LightweightDefinitionKind} from './lightweight-definition-kind';
|
||||
import {LightweightType} from './lightweight-type';
|
||||
/**
|
||||
* Represents an enum definition
|
||||
*/
|
||||
export interface LightweightAliasDefinition extends LightweightDefinitionBase {
|
||||
/**
|
||||
* Kind
|
||||
*/
|
||||
kind: LightweightDefinitionKind.ALIAS_LIKE;
|
||||
|
||||
/**
|
||||
* Enumeration or union values
|
||||
*/
|
||||
type?: LightweightType;
|
||||
}
|
||||
54
src/easy-ast/types/lightweight-class-definition.d.ts
vendored
Normal file
54
src/easy-ast/types/lightweight-class-definition.d.ts
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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 {LightweightDefinitionBase} from './lightweight-definition';
|
||||
import {LightweightDefinitionKind} from './lightweight-definition-kind';
|
||||
import {LightweightIndexSignature, LightweightProperty} from './lightweight-property';
|
||||
import {LightweightType} from './lightweight-type';
|
||||
|
||||
/**
|
||||
* Represents a class definition
|
||||
*/
|
||||
export interface LightweightClassDefinition extends LightweightDefinitionBase {
|
||||
/**
|
||||
* String values of the extended definitions
|
||||
*/
|
||||
extendedDefinitions?: LightweightType[];
|
||||
|
||||
/**
|
||||
* String values of the implemented definitions
|
||||
*/
|
||||
implementedDefinitions?: LightweightType[];
|
||||
|
||||
/**
|
||||
* Index signatures
|
||||
*/
|
||||
indexSignatures?: Record<string, LightweightIndexSignature>;
|
||||
|
||||
/**
|
||||
* Kind
|
||||
*/
|
||||
kind: LightweightDefinitionKind.CLASS_LIKE;
|
||||
|
||||
/**
|
||||
* Properties of the definition
|
||||
*/
|
||||
properties?: Record<string, LightweightProperty>;
|
||||
|
||||
/**
|
||||
* Generic type parameters of this class
|
||||
*/
|
||||
typeParameters?: string[];
|
||||
}
|
||||
48
src/easy-ast/types/lightweight-comment.d.ts
vendored
Normal file
48
src/easy-ast/types/lightweight-comment.d.ts
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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 a Comment
|
||||
*/
|
||||
export interface LightweightComment {
|
||||
/**
|
||||
* Description of the comment
|
||||
*/
|
||||
description?: string;
|
||||
|
||||
/**
|
||||
* Short summary of the comment
|
||||
*/
|
||||
shortSummary?: string;
|
||||
|
||||
/**
|
||||
* Tags of the comment
|
||||
*/
|
||||
tags?: LightweightCommentTag[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Lightweight comment tag
|
||||
*/
|
||||
export interface LightweightCommentTag {
|
||||
/**
|
||||
* The name of the tag
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* The parameters of the tag
|
||||
*/
|
||||
parameters?: string[];
|
||||
}
|
||||
19
src/easy-ast/types/lightweight-definition-kind.ts
Normal file
19
src/easy-ast/types/lightweight-definition-kind.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
export enum LightweightDefinitionKind {
|
||||
CLASS_LIKE = 'class-like',
|
||||
ALIAS_LIKE = 'alias-like',
|
||||
}
|
||||
46
src/easy-ast/types/lightweight-definition.d.ts
vendored
Normal file
46
src/easy-ast/types/lightweight-definition.d.ts
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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 {LightweightDefinitionKind} from './lightweight-definition-kind';
|
||||
import {LightweightComment} from './lightweight-comment';
|
||||
import {LightweightClassDefinition} from './lightweight-class-definition';
|
||||
import {LightweightAliasDefinition} from './lightweight-alias-definition';
|
||||
|
||||
export type LightweightDefinition = LightweightClassDefinition | LightweightAliasDefinition;
|
||||
|
||||
/**
|
||||
* Represents any definition without specifics
|
||||
*/
|
||||
export interface LightweightDefinitionBase {
|
||||
/**
|
||||
* The comment of the definition
|
||||
*/
|
||||
comment?: LightweightComment;
|
||||
|
||||
/**
|
||||
* Kind of the definition
|
||||
*/
|
||||
kind: LightweightDefinitionKind;
|
||||
|
||||
/**
|
||||
* The definition type
|
||||
* e.g. [`abstract`, `class`] or [`enum`] or [`export`, `type`]
|
||||
*/
|
||||
modifiers?: string[];
|
||||
|
||||
/**
|
||||
* Name of the definition
|
||||
*/
|
||||
name: string;
|
||||
}
|
||||
100
src/easy-ast/types/lightweight-project.ts
Normal file
100
src/easy-ast/types/lightweight-project.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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 {assign, cloneDeep, flatMap, fromPairs, trimEnd} from 'lodash';
|
||||
import {mapNotNil} from '../../util/collections';
|
||||
import {definitionsOf, isLightweightClass} from '../ast-util';
|
||||
import {lightweightProjectFromPath} from '../easy-ast';
|
||||
import {LightweightClassDefinition} from './lightweight-class-definition';
|
||||
import {LightweightDefinition} from './lightweight-definition';
|
||||
|
||||
/**
|
||||
* Build an index for a lightweight project
|
||||
*/
|
||||
function buildIndex(project: LightweightProject): Record<string, string> {
|
||||
return fromPairs(
|
||||
flatMap(project, (definitions, file) => Object.keys(definitions).map(definition => [definition, file])),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A lightweight definition class for more advanced use cases
|
||||
*/
|
||||
export class LightweightProjectWithIndex {
|
||||
/**
|
||||
* All definitions
|
||||
*/
|
||||
readonly definitions: Record<string, LightweightDefinition>;
|
||||
|
||||
/**
|
||||
* Project
|
||||
*/
|
||||
readonly files: LightweightProject;
|
||||
|
||||
/**
|
||||
* Index of all definitions to their respective files
|
||||
*/
|
||||
readonly index: {
|
||||
[definitionName: string]: string;
|
||||
};
|
||||
|
||||
constructor(project: LightweightProject | string) {
|
||||
this.files = typeof project === 'string' ? lightweightProjectFromPath(project) : project;
|
||||
this.index = buildIndex(this.files);
|
||||
this.definitions = definitionsOf(this.files);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply inherited classes; default deeply
|
||||
*/
|
||||
applyInheritance(classLike: LightweightClassDefinition, deep?: boolean): LightweightDefinition {
|
||||
return assign(
|
||||
mapNotNil(
|
||||
[...(classLike.implementedDefinitions ?? []), ...(classLike.extendedDefinitions ?? [])],
|
||||
extension => {
|
||||
const object = this.definitions[extension.referenceName ?? ''];
|
||||
|
||||
return (deep ?? true) && isLightweightClass(object)
|
||||
? this.applyInheritance(object)
|
||||
: cloneDeep(object);
|
||||
},
|
||||
),
|
||||
cloneDeep(classLike),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a definition
|
||||
*
|
||||
* Requires the program to be run with `--require ts-node/register`
|
||||
*/
|
||||
async instantiateDefinitionByName<T>(name: string, findCompiledModule = true): Promise<T | undefined> {
|
||||
const fsPath = this.index[name];
|
||||
if (typeof fsPath === 'undefined') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const module = await import(findCompiledModule ? `${trimEnd(fsPath, 'd.ts')}.js` : fsPath);
|
||||
|
||||
return new module[name]() as T;
|
||||
}
|
||||
}
|
||||
|
||||
export interface LightweightFile {
|
||||
[definitionName: string]: LightweightDefinition;
|
||||
}
|
||||
|
||||
export interface LightweightProject {
|
||||
[sourcePath: string]: LightweightFile;
|
||||
}
|
||||
53
src/easy-ast/types/lightweight-property.d.ts
vendored
Normal file
53
src/easy-ast/types/lightweight-property.d.ts
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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 {LightweightComment} from './lightweight-comment';
|
||||
import {LightweightType} from './lightweight-type';
|
||||
|
||||
/**
|
||||
* Represents a property definition
|
||||
*/
|
||||
export interface LightweightProperty {
|
||||
/**
|
||||
* The comment of the property
|
||||
*/
|
||||
comment?: LightweightComment;
|
||||
|
||||
/**
|
||||
* Name of the property
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* Is the property marked as optional
|
||||
*/
|
||||
optional?: true;
|
||||
|
||||
/**
|
||||
* A record of properties if the property happens to be a type literal
|
||||
*/
|
||||
properties?: Record<string, LightweightProperty>;
|
||||
|
||||
/**
|
||||
* Type of the property
|
||||
*/
|
||||
type: LightweightType;
|
||||
}
|
||||
|
||||
export interface LightweightIndexSignature extends LightweightProperty {
|
||||
/**
|
||||
* Type of the index signature, if it is an index signature
|
||||
*/
|
||||
indexSignatureType: LightweightType;
|
||||
}
|
||||
58
src/easy-ast/types/lightweight-type.d.ts
vendored
Normal file
58
src/easy-ast/types/lightweight-type.d.ts
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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 {TypeFlags} from 'typescript';
|
||||
|
||||
/**
|
||||
* Describes an easy to use type definition.
|
||||
*/
|
||||
export interface LightweightType {
|
||||
/**
|
||||
* Type Flags
|
||||
*/
|
||||
flags: TypeFlags;
|
||||
|
||||
/**
|
||||
* Contains all types inside of <> brackets
|
||||
*/
|
||||
genericsTypes?: LightweightType[];
|
||||
|
||||
/**
|
||||
* If it is an array(-like) type
|
||||
*/
|
||||
isArray?: true;
|
||||
|
||||
/**
|
||||
* If it is a type parameter
|
||||
*/
|
||||
isTypeParameter?: true;
|
||||
|
||||
/**
|
||||
* The name of the type that is referenced. Enum members have reference names that lead no where.
|
||||
*/
|
||||
referenceName?: string;
|
||||
|
||||
/**
|
||||
* Type specifications, if the type is combined by either an array, union or a typeOperator
|
||||
*/
|
||||
specificationTypes?: LightweightType[];
|
||||
|
||||
/**
|
||||
* Value of the type
|
||||
*
|
||||
* Literal types have their value here, non-literals their type name (for example 'string')
|
||||
*/
|
||||
value?: string | number | boolean;
|
||||
}
|
||||
Reference in New Issue
Block a user