UNPKG

code-graph-generator

Version:

Generate Json Object of code that can be used to generate code-graphs for JavaScript/TypeScript/Range projects

448 lines 19.3 kB
"use strict"; // // src/parser/ts-parser.ts // import { parse } from '@babel/parser'; // import path from 'path'; // import { FileGraph, FileParser, TypeGraph, MemberGraph } from '../types/interfaces'; // import { getStartLine, countLines, lightTraverse, normalizePath } from '../utils/ast-utils'; // import { logger } from '../utils/file-utils'; // import { createJsParser } from './js-parser'; Object.defineProperty(exports, "__esModule", { value: true }); exports.createTsParser = createTsParser; // export function createTsParser(): FileParser { // const jsParser = createJsParser(); // return { // async parse(filePath: string, content: string): Promise<FileGraph> { // logger.debug(`TS Parser: parsing ${filePath}`); // // Normalize the filePath before passing to jsParser // const normalizedPath = normalizePath(filePath); // const fileGraph = await jsParser.parse(normalizedPath, content); // try { // const ast = parse(content, { // sourceType: 'module', // plugins: ['typescript', 'classProperties', 'dynamicImport', 'objectRestSpread', 'optionalChaining', 'decorators-legacy'], // errorRecovery: true, // }); // extractTypeDefinitions(ast, fileGraph, normalizedPath); // // Add this call to update function return types // updateFunctionTypes(ast, fileGraph); // return fileGraph; // } catch (error) { // logger.warn(`TypeScript parsing failed for ${normalizedPath}, using JS parser result:`, error); // return fileGraph; // } // } // }; // } // // New function to update function return types from TypeScript annotations // function updateFunctionTypes(ast: any, fileGraph: FileGraph): void { // const visitors = { // FunctionDeclaration: (node: any) => { // if (node.id && node.id.name && node.returnType) { // const functionName = node.id.name; // const func = fileGraph.functions.find(f => f.name === functionName); // if (func && node.returnType) { // const returnType = extractTypeAnnotation(node.returnType); // if (returnType && !func.types.includes(returnType)) { // func.types = [returnType]; // Replace any existing types // } // } // } // }, // ExportNamedDeclaration: (node: any) => { // if (node.declaration && node.declaration.type === 'FunctionDeclaration') { // const funcNode = node.declaration; // if (funcNode.id && funcNode.id.name && funcNode.returnType) { // const functionName = funcNode.id.name; // const func = fileGraph.functions.find(f => f.name === functionName); // if (func && funcNode.returnType) { // const returnType = extractTypeAnnotation(funcNode.returnType); // if (returnType && !func.types.includes(returnType)) { // func.types = [returnType]; // Replace any existing types // } // } // } // } // }, // ArrowFunctionExpression: (node: any) => { // // For functions stored in variables // if (node.returnType) { // // Need to find the variable name // let current = node; // let parent: any = null; // // Simple traversal to find parent VariableDeclarator // const varVisitor = { // VariableDeclarator: (varNode: any) => { // if (varNode.init === node && varNode.id && varNode.id.type === 'Identifier') { // parent = varNode; // } // } // }; // lightTraverse(ast, varVisitor); // if (parent && parent.id && parent.id.name) { // const functionName = parent.id.name; // const func = fileGraph.functions.find(f => f.name === functionName); // if (func) { // const returnType = extractTypeAnnotation(node.returnType); // if (returnType && !func.types.includes(returnType)) { // func.types = [returnType]; // Replace any existing types // } // } // } // } // } // }; // lightTraverse(ast, visitors); // } // function extractTypeDefinitions(ast: any, fileGraph: FileGraph, filePath: string): void { // const visitors = { // TSInterfaceDeclaration: (node: any) => { // if (node.id && node.id.name) { // const name = node.id.name; // const startLine = getStartLine(node); // const typeGraph: TypeGraph = { // name, // file: filePath, // startLine, // length: node.body ? (node.body.loc.end.line - node.body.loc.start.line + 1) : 0, // properties: [], // }; // if (node.body && node.body.body) { // node.body.body.forEach((prop: any) => { // extractTypeProperty(prop, typeGraph); // }); // } // fileGraph.types.push(typeGraph); // } // }, // TSTypeAliasDeclaration: (node: any) => { // if (node.id && node.id.name) { // const name = node.id.name; // const startLine = getStartLine(node); // const typeGraph: TypeGraph = { // name, // file: filePath, // startLine, // length: 1, // properties: [], // }; // if (node.typeAnnotation && node.typeAnnotation.type === 'TSTypeLiteral') { // node.typeAnnotation.members.forEach((prop: any) => { // extractTypeProperty(prop, typeGraph); // }); // } // fileGraph.types.push(typeGraph); // } // }, // TSEnumDeclaration: (node: any) => { // if (node.id && node.id.name) { // const name = node.id.name; // const startLine = getStartLine(node); // const typeGraph: TypeGraph = { // name, // file: filePath, // startLine, // length: node.members ? (node.loc.end.line - node.loc.start.line + 1) : 0, // properties: [], // }; // if (node.members) { // node.members.forEach((member: any) => { // if (member.id && (member.id.type === 'Identifier' || member.id.type === 'StringLiteral')) { // const memberName = member.id.type === 'Identifier' ? member.id.name : member.id.value; // const memberGraph: MemberGraph = { // name: memberName, // type: 'enum', // }; // typeGraph.properties.push(memberGraph); // } // }); // } // fileGraph.types.push(typeGraph); // } // } // }; // lightTraverse(ast, visitors); // } // function extractTypeProperty(prop: any, typeGraph: TypeGraph): void { // if (prop.type === 'TSPropertySignature' && prop.key) { // const name = prop.key.name || prop.key.value; // const memberGraph: MemberGraph = { // name, // type: extractTypeAnnotation(prop.typeAnnotation), // }; // typeGraph.properties.push(memberGraph); // } // else if (prop.type === 'TSMethodSignature' && prop.key) { // const name = prop.key.name || prop.key.value; // const parameters = prop.parameters ? // prop.parameters.map((param: any) => extractTypeAnnotation(param.typeAnnotation)) : // []; // const memberGraph: MemberGraph = { // name, // type: extractTypeAnnotation(prop.typeAnnotation), // parameters, // }; // typeGraph.properties.push(memberGraph); // } // } // function extractTypeAnnotation(typeAnnotation: any): string | undefined { // if (!typeAnnotation) return undefined; // const typeNode = typeAnnotation.typeAnnotation; // if (!typeNode) return undefined; // switch (typeNode.type) { // case 'TSStringKeyword': // return 'string'; // case 'TSNumberKeyword': // return 'number'; // case 'TSBooleanKeyword': // return 'boolean'; // case 'TSArrayType': // const elementType = extractTypeAnnotation({ typeAnnotation: typeNode.elementType }); // return `${elementType}[]`; // case 'TSTypeReference': // if (typeNode.typeName) { // return typeNode.typeName.name; // } // return 'object'; // default: // return 'any'; // } // } // src/parser/ts-parser.ts const parser_1 = require("@babel/parser"); const ast_utils_1 = require("../utils/ast-utils"); const file_utils_1 = require("../utils/file-utils"); const js_parser_1 = require("./js-parser"); function createTsParser() { const jsParser = (0, js_parser_1.createJsParser)(); return { async parse(filePath, content) { file_utils_1.logger.debug(`TS Parser: parsing ${filePath}`); // Preserve original file path but normalize slashes const normalizedPath = (0, ast_utils_1.normalizePath)(filePath); const fileGraph = await jsParser.parse(normalizedPath, content); try { const ast = (0, parser_1.parse)(content, { sourceType: 'module', plugins: ['typescript', 'classProperties', 'dynamicImport', 'objectRestSpread', 'optionalChaining', 'decorators-legacy'], errorRecovery: true, }); extractTypeDefinitions(ast, fileGraph, normalizedPath); // Extract function return types from TypeScript annotations extractFunctionReturnTypes(ast, fileGraph); return fileGraph; } catch (error) { file_utils_1.logger.warn(`TypeScript parsing failed for ${normalizedPath}, using JS parser result:`, error); return fileGraph; } } }; } /** * Extracts return types from TypeScript functions */ function extractFunctionReturnTypes(ast, fileGraph) { const visitors = { // Handle function declarations FunctionDeclaration: (node) => { if (node.id?.name && node.returnType?.typeAnnotation) { const functionName = node.id.name; const returnType = getTypeFromAnnotation(node.returnType.typeAnnotation); // Find the function in the file graph const func = fileGraph.functions.find(f => f.name === functionName); if (func && returnType) { // Add the return type if (!func.types) { func.types = []; } if (!func.types.includes(returnType)) { func.types = [returnType]; // Replace any existing types } } } }, // Handle exported function declarations ExportNamedDeclaration: (node) => { if (node.declaration?.type === 'FunctionDeclaration' && node.declaration.id?.name && node.declaration.returnType?.typeAnnotation) { const functionName = node.declaration.id.name; const returnType = getTypeFromAnnotation(node.declaration.returnType.typeAnnotation); // Find the function in the file graph const func = fileGraph.functions.find(f => f.name === functionName); if (func && returnType) { // Add the return type if (!func.types) { func.types = []; } if (!func.types.includes(returnType)) { func.types = [returnType]; // Replace existing types } } } }, // Handle arrow functions in variable declarations VariableDeclarator: (node) => { if (node.id?.type === 'Identifier' && node.init?.type === 'ArrowFunctionExpression' && node.init.returnType?.typeAnnotation) { const functionName = node.id.name; const returnType = getTypeFromAnnotation(node.init.returnType.typeAnnotation); // Find the function in the file graph const func = fileGraph.functions.find(f => f.name === functionName); if (func && returnType) { // Add the return type if (!func.types) { func.types = []; } if (!func.types.includes(returnType)) { func.types = [returnType]; // Replace existing types } } } } }; // Traverse the AST (0, ast_utils_1.lightTraverse)(ast, visitors); } /** * Gets a TypeScript type as a string from a type annotation */ function getTypeFromAnnotation(typeNode) { if (!typeNode) return undefined; switch (typeNode.type) { case 'TSStringKeyword': return 'string'; case 'TSNumberKeyword': return 'number'; case 'TSBooleanKeyword': return 'boolean'; case 'TSVoidKeyword': return 'void'; case 'TSArrayType': const elementType = getTypeFromAnnotation(typeNode.elementType); return elementType ? `${elementType}[]` : undefined; case 'TSTypeReference': return typeNode.typeName?.name; default: return undefined; } } function extractTypeDefinitions(ast, fileGraph, filePath) { const visitors = { TSInterfaceDeclaration: (node) => { if (node.id && node.id.name) { const name = node.id.name; const startLine = (0, ast_utils_1.getStartLine)(node); const typeGraph = { name, file: filePath, startLine, length: node.body ? (node.body.loc.end.line - node.body.loc.start.line + 1) : 0, properties: [], }; if (node.body && node.body.body) { node.body.body.forEach((prop) => { extractTypeProperty(prop, typeGraph); }); } fileGraph.types.push(typeGraph); } }, TSTypeAliasDeclaration: (node) => { if (node.id && node.id.name) { const name = node.id.name; const startLine = (0, ast_utils_1.getStartLine)(node); const typeGraph = { name, file: filePath, startLine, length: 1, properties: [], }; if (node.typeAnnotation && node.typeAnnotation.type === 'TSTypeLiteral') { node.typeAnnotation.members.forEach((prop) => { extractTypeProperty(prop, typeGraph); }); } fileGraph.types.push(typeGraph); } }, TSEnumDeclaration: (node) => { if (node.id && node.id.name) { const name = node.id.name; const startLine = (0, ast_utils_1.getStartLine)(node); const typeGraph = { name, file: filePath, startLine, length: node.members ? (node.loc.end.line - node.loc.start.line + 1) : 0, properties: [], }; if (node.members) { node.members.forEach((member) => { if (member.id && (member.id.type === 'Identifier' || member.id.type === 'StringLiteral')) { const memberName = member.id.type === 'Identifier' ? member.id.name : member.id.value; const memberGraph = { name: memberName, type: 'enum', }; typeGraph.properties.push(memberGraph); } }); } fileGraph.types.push(typeGraph); } } }; (0, ast_utils_1.lightTraverse)(ast, visitors); } function extractTypeProperty(prop, typeGraph) { if (prop.type === 'TSPropertySignature' && prop.key) { const name = prop.key.name || prop.key.value; const memberGraph = { name, type: extractTypeAnnotation(prop.typeAnnotation), }; typeGraph.properties.push(memberGraph); } else if (prop.type === 'TSMethodSignature' && prop.key) { const name = prop.key.name || prop.key.value; const parameters = prop.parameters ? prop.parameters.map((param) => extractTypeAnnotation(param.typeAnnotation)) : []; const memberGraph = { name, type: extractTypeAnnotation(prop.typeAnnotation), parameters, }; typeGraph.properties.push(memberGraph); } } function extractTypeAnnotation(typeAnnotation) { if (!typeAnnotation) return undefined; const typeNode = typeAnnotation.typeAnnotation; if (!typeNode) return undefined; switch (typeNode.type) { case 'TSStringKeyword': return 'string'; case 'TSNumberKeyword': return 'number'; case 'TSBooleanKeyword': return 'boolean'; case 'TSArrayType': const elementType = extractTypeAnnotation({ typeAnnotation: typeNode.elementType }); return `${elementType}[]`; case 'TSTypeReference': if (typeNode.typeName) { return typeNode.typeName.name; } return 'object'; default: return 'any'; } } //# sourceMappingURL=ts-parser.js.map