From 0ef343c13d35a4e557b9ee8815ce993d38d1b728 Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Fri, 30 Jan 2026 01:01:09 +0000 Subject: [PATCH] Add utility modules --- src/utils/typeUtils.ts | 142 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 src/utils/typeUtils.ts diff --git a/src/utils/typeUtils.ts b/src/utils/typeUtils.ts new file mode 100644 index 0000000..1207b2c --- /dev/null +++ b/src/utils/typeUtils.ts @@ -0,0 +1,142 @@ +import type * as TSESTree from '@typescript-eslint/typescript-estree'; + +export function getTypeName(node: TSESTree.Node): string | null { + switch (node.type) { + case 'TSTypeReference': + if (node.typeName.type === 'Identifier') { + return node.typeName.name; + } + break; + case 'TSQualifiedName': + if (node.left.type === 'Identifier' && node.right.type === 'Identifier') { + return `${node.left.name}.${node.right.name}`; + } + break; + case 'Identifier': + return node.name; + case 'TSInterfaceDeclaration': + return node.id.name; + case 'TSTypeAliasDeclaration': + return node.id.name; + case 'TSEnumDeclaration': + return node.id.name; + case 'ClassDeclaration': + return node.id?.name; + } + return null; +} + +export function extractTypeAnnotation(node: TSESTree.Node): string { + if (node.typeAnnotation && node.typeAnnotation.typeAnnotation) { + return typeToString(node.typeAnnotation.typeAnnotation); + } + return ''; +} + +export function typeToString(node: TSESTree.Node): string { + switch (node.type) { + case 'TSStringKeyword': + return 'string'; + case 'TSNumberKeyword': + return 'number'; + case 'TSBooleanKeyword': + return 'boolean'; + case 'TSNullKeyword': + return 'null'; + case 'TSUndefinedKeyword': + return 'undefined'; + case 'TSVoidKeyword': + return 'void'; + case 'TSNeverKeyword': + return 'never'; + case 'TSAnyKeyword': + return 'any'; + case 'TSUnknownKeyword': + return 'unknown'; + case 'TSBigIntKeyword': + return 'bigint'; + case 'TSSymbolKeyword': + return 'symbol'; + case 'TSArrayType': + return `${typeToString(node.elementType)}[]`; + case 'TSTupleType': + return `[${node.elementTypes.map((el: TSESTree.TSTupleElement) => typeToString(el)).join(', ')}]`; + case 'TSUnionType': + return node.types.map((t: TSESTree.Node) => typeToString(t)).join(' | '); + case 'TSIntersectionType': + return node.types.map((t: TSESTree.Node) => typeToString(t)).join(' & '); + case 'TSTypeReference': + if (node.typeName.type === 'Identifier') { + const typeArgs = node.typeParameters?.params + ? `<${node.typeParameters.params.map((p: TSESTree.Node) => typeToString(p)).join(', ')}>` + : ''; + return `${node.typeName.name}${typeArgs}`; + } + break; + case 'TSTypeLiteral': + return `{ ${node.members.map((m: TSESTree.Node) => typeToString(m)).join('; ')} }`; + case 'TSPropertySignature': { + const key = node.key.type === 'Identifier' ? node.key.name : typeToString(node.key); + const optional = node.optional ? '?' : ''; + return `${key}${optional}: ${typeToString(node.typeAnnotation?.typeAnnotation || node)}`; + } + case 'TSCallSignatureDeclaration': + return '()'; + case 'TSConstructSignatureDeclaration': + return 'new()'; + case 'TSMethodSignature': { + const methodName = node.key.type === 'Identifier' ? node.key.name : typeToString(node.key); + return `${methodName}()`; + } + case 'TSQualifiedName': + return `${typeToString(node.left)}.${typeToString(node.right)}`; + case 'TSParenthesizedType': + return `(${typeToString(node.typeAnnotation)})`; + case 'TSNonNullExpression': + return `${typeToString(node.expression)}`; + case 'TSTypeQuery': + if (node.exprName.type === 'Identifier') { + return `typeof ${node.exprName.name}`; + } + break; + case 'TSMappedType': { + const typeParam = node.typeParameter.name.constraint + ? `${node.typeParameter.name}: ${typeToString(node.typeParameter.constraint!)}` + : node.typeParameter.name; + const asClause = node.asClause + ? ` as ${typeToString(node.asClause)}` + : ''; + return `{ [${typeParam}${asClause}]${node.optional ? '?' : ''} in ${typeToString(node.typeParameter.constraint || node.typeParameter)} }`; + } + case 'TSIndexedAccessType': + return `${typeToString(node.objectType)}[${typeToString(node.indexType)}]`; + case 'TSConditionalType': + return `${typeToString(node.checkType)} extends ${typeToString(node.extendsType)} ? ${typeToString(node.trueType)} : ${typeToString(node.falseType)}`; + case 'TSInferType': + return `infer ${node.typeParameter.name}`; + case 'TSTemplateLiteralType': + return `\`${node.quasis.map((q: TSESTree.TemplateLiteral) => q.value.raw).join('')}\``; + } + return 'unknown'; +} + +export function isExternalModule(name: string): boolean { + return name.startsWith('@') || !name.startsWith('.') && !name.startsWith('/'); +} + +export function normalizeModuleName(moduleName: string, basePath: string): string { + if (isExternalModule(moduleName)) { + return moduleName; + } + return path.resolve(basePath, moduleName); +} + +import * as path from 'path'; + +export function getNodeId(node: TSESTree.Node, filePath: string): string { + const name = getTypeName(node); + if (name) { + return `${filePath}#${name}`; + } + return `${filePath}#${node.range[0]}-${node.range[1]}`; +}