From b0882161478498cc1fc329de44c6cdca17a6d0c8 Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Fri, 30 Jan 2026 00:57:43 +0000 Subject: [PATCH] Add parser modules --- src/parsers/interfaceParser.ts | 156 +++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 src/parsers/interfaceParser.ts diff --git a/src/parsers/interfaceParser.ts b/src/parsers/interfaceParser.ts new file mode 100644 index 0000000..2b3151b --- /dev/null +++ b/src/parsers/interfaceParser.ts @@ -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; + 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( + 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); +}