This commit is contained in:
156
src/parsers/interfaceParser.ts
Normal file
156
src/parsers/interfaceParser.ts
Normal file
@@ -0,0 +1,156 @@
|
||||
import { createASTUtils } from '../utils/astUtils';
|
||||
import { typeToString } from '../utils/typeUtils';
|
||||
import { InterfaceType, InterfaceMember, GenericParameter } from '../types';
|
||||
import type * as TSESTree from '@typescript-eslint/typescript-estree';
|
||||
|
||||
export interface InterfaceParserOptions {
|
||||
includeInheritedMembers?: boolean;
|
||||
skipMethods?: boolean;
|
||||
skipIndexSignatures?: boolean;
|
||||
}
|
||||
|
||||
export class InterfaceParser {
|
||||
private astUtils: ReturnType<typeof createASTUtils>;
|
||||
private options: InterfaceParserOptions;
|
||||
|
||||
constructor(options: InterfaceParserOptions = {}) {
|
||||
this.astUtils = createASTUtils();
|
||||
this.options = {
|
||||
includeInheritedMembers: options.includeInheritedMembers ?? false,
|
||||
skipMethods: options.skipMethods ?? false,
|
||||
skipIndexSignatures: options.skipIndexSignatures ?? false
|
||||
};
|
||||
}
|
||||
|
||||
parse(source: string, filePath: string): InterfaceType[] {
|
||||
const ast = this.astUtils.parse(source, filePath);
|
||||
const interfaces = this.astUtils.getInterfaces(ast);
|
||||
|
||||
return interfaces.map((iface) => this.parseInterface(iface));
|
||||
}
|
||||
|
||||
parseInterface(node: any): InterfaceType {
|
||||
const members = this.parseMembers(node.body.body);
|
||||
const extendsNames = this.parseExtends(node.extends);
|
||||
const generics = this.parseGenericParameters(node.typeParameters);
|
||||
|
||||
const dependencies = this.extractDependencies(node);
|
||||
|
||||
return {
|
||||
name: node.id.name,
|
||||
filePath: '',
|
||||
startLine: node.loc?.start?.line ?? 0,
|
||||
endLine: node.loc?.end?.line ?? 0,
|
||||
kind: 'interface',
|
||||
members,
|
||||
extends: extendsNames,
|
||||
generics,
|
||||
dependencies,
|
||||
rawNode: node
|
||||
};
|
||||
}
|
||||
|
||||
private parseMembers(members: any[]): InterfaceMember[] {
|
||||
return members
|
||||
.filter((m) => {
|
||||
if (this.options.skipMethods && (m.type === 'TSMethodSignature')) {
|
||||
return false;
|
||||
}
|
||||
if (this.options.skipIndexSignatures && (m.type === 'TSIndexedAccessType')) {
|
||||
return false;
|
||||
}
|
||||
return m.type === 'TSPropertySignature' || m.type === 'TSMethodSignature';
|
||||
})
|
||||
.map((m) => this.parseMember(m));
|
||||
}
|
||||
|
||||
private parseMember(node: any): InterfaceMember {
|
||||
const key = node.key;
|
||||
const name = key.type === 'Identifier' ? key.name : typeToString(key);
|
||||
|
||||
let type = 'unknown';
|
||||
if (node.typeAnnotation) {
|
||||
type = typeToString(node.typeAnnotation.typeAnnotation);
|
||||
} else if (node.type === 'TSMethodSignature') {
|
||||
const params = (node as any).params
|
||||
.map((p: any) => typeToString(p))
|
||||
.join(', ');
|
||||
type = `(${params}) => void`;
|
||||
}
|
||||
|
||||
return {
|
||||
name,
|
||||
type,
|
||||
isOptional: node.optional ?? false,
|
||||
isReadonly: (node as any).readonly ?? false,
|
||||
modifiers: []
|
||||
};
|
||||
}
|
||||
|
||||
private parseExtends(extendsClause: any[] | null): string[] {
|
||||
if (!extendsClause) {
|
||||
return [];
|
||||
}
|
||||
return extendsClause.map((ext) => {
|
||||
if (ext.expression.type === 'Identifier') {
|
||||
return ext.expression.name;
|
||||
}
|
||||
return typeToString(ext.expression);
|
||||
});
|
||||
}
|
||||
|
||||
private parseGenericParameters(params: any | null): GenericParameter[] {
|
||||
if (!params || !params.params) {
|
||||
return [];
|
||||
}
|
||||
return params.params.map((param: TSESTree.TSTypeParameter) => ({
|
||||
name: param.name.name,
|
||||
constraint: param.constraint ? typeToString(param.constraint) : undefined,
|
||||
default: param.default ? typeToString(param.default) : undefined
|
||||
}));
|
||||
}
|
||||
|
||||
private extractDependencies(node: TSESTree.TSInterfaceDeclaration): string[] {
|
||||
const dependencies: string[] = [];
|
||||
|
||||
if (node.extends) {
|
||||
for (const ext of node.extends) {
|
||||
if (ext.expression.type === 'Identifier') {
|
||||
dependencies.push(ext.expression.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const typeRefs = this.astUtils.findNodes<TSESTree.TSTypeReference>(
|
||||
node,
|
||||
(n): n is TSESTree.TSTypeReference => n.type === 'TSTypeReference'
|
||||
);
|
||||
|
||||
for (const ref of typeRefs) {
|
||||
if (ref.typeName.type === 'Identifier') {
|
||||
const name = ref.typeName.name;
|
||||
if (!dependencies.includes(name)) {
|
||||
dependencies.push(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
getMemberCount(interfaceType: InterfaceType): number {
|
||||
return interfaceType.members.length;
|
||||
}
|
||||
|
||||
getPropertyCount(interfaceType: InterfaceType): number {
|
||||
return interfaceType.members.filter((_m) => true).length;
|
||||
}
|
||||
|
||||
hasMethod(interfaceType: InterfaceType, methodName: string): boolean {
|
||||
return interfaceType.members.some((m) => m.name === methodName);
|
||||
}
|
||||
}
|
||||
|
||||
export function createInterfaceParser(options?: InterfaceParserOptions): InterfaceParser {
|
||||
return new InterfaceParser(options);
|
||||
}
|
||||
Reference in New Issue
Block a user