UNPKG

swagger-auto-generate

Version:

Automatically generate Swagger JSDoc documentation for Express applications

243 lines 8.19 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ASTParser = void 0; const parser_1 = require("@babel/parser"); class ASTParser { /** * Parse a JavaScript/TypeScript file and extract route information */ static parseFile(content, filePath) { try { const ast = (0, parser_1.parse)(content, { sourceType: 'module', plugins: ['typescript', 'jsx'], errorRecovery: true, }); const routes = []; const functions = this.extractFunctions(ast, content); for (const func of functions) { if (func.isRouteHandler && func.httpMethod && func.routePath) { const routeInfo = this.createRouteInfo(func, filePath); if (routeInfo) { routes.push(routeInfo); } } } return routes; } catch (error) { console.warn(`Warning: Could not parse file ${filePath}:`, error); return []; } } /** * Recursively extract functions from Babel AST */ static extractFunctions(node, content) { const functions = []; if (!node || typeof node !== 'object') return functions; // Check for function nodes if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') { let func = null; if (node.type === 'ArrowFunctionExpression') { func = this.parseArrowFunction(node, content); } else { func = this.parseFunction(node, content); } if (func) functions.push(func); } // Recursively traverse all child properties for (const key in node) { if (node.hasOwnProperty(key)) { const child = node[key]; if (Array.isArray(child)) { for (const c of child) { functions.push(...this.extractFunctions(c, content)); } } else if (typeof child === 'object' && child !== null && child.type) { functions.push(...this.extractFunctions(child, content)); } } } return functions; } /** * Parse a function declaration or expression */ static parseFunction(node, content) { if (!node.params || !node.body) return null; const functionName = node.id?.name || 'anonymous'; const parameters = node.params.map((param) => param.name || 'param'); const body = content.substring(node.body.start, node.body.end); const lineNumber = node.loc?.start.line || 0; const isRouteHandler = this.isRouteHandler(parameters); const { httpMethod, routePath } = this.extractRouteInfo(node, content); return { name: functionName, parameters, body, lineNumber, isRouteHandler, httpMethod, routePath, }; } /** * Parse an arrow function */ static parseArrowFunction(node, content) { if (!node.params || !node.body) return null; const parameters = node.params.map((param) => param.name || 'param'); const body = content.substring(node.body.start, node.body.end); const lineNumber = node.loc?.start.line || 0; const isRouteHandler = this.isRouteHandler(parameters); const { httpMethod, routePath } = this.extractRouteInfo(node, content); return { name: 'anonymous', parameters, body, lineNumber, isRouteHandler, httpMethod, routePath, }; } /** * Check if a function is a route handler */ static isRouteHandler(parameters) { return parameters.some(param => param === 'req' || param === 'request' || param.includes('req') || param.includes('request')) && parameters.some(param => param === 'res' || param === 'response' || param.includes('res') || param.includes('response')); } /** * Extract route information from function */ static extractRouteInfo(node, content) { // Look for common Express route patterns const patterns = [ /\.(get|post|put|delete|patch|all)\s*\(\s*['"`]([^'"`]+)['"`]/g, /\.(get|post|put|delete|patch|all)\s*\(\s*`([^`]+)`/g, ]; const nodeContent = content.substring(node.start, node.end); for (const pattern of patterns) { const matches = [...nodeContent.matchAll(pattern)]; if (matches.length > 0) { const match = matches[0]; return { httpMethod: match[1].toLowerCase(), routePath: match[2], }; } } return {}; } /** * Create RouteInfo from ParsedFunction */ static createRouteInfo(func, filePath) { if (!func.httpMethod || !func.routePath) return null; const parameters = this.extractParameters(func); const responses = this.extractResponses(func); return { method: func.httpMethod, path: func.routePath, functionName: func.name, parameters, responses, filePath, lineNumber: func.lineNumber, summary: `Auto-generated documentation for ${func.name}`, description: `Endpoint for ${func.httpMethod.toUpperCase()} ${func.routePath}`, }; } /** * Extract parameters from function */ static extractParameters(func) { const parameters = []; // Extract path parameters from route path const pathParams = func.routePath?.match(/:[^/]+/g) || []; for (const param of pathParams) { const paramName = param.substring(1); parameters.push({ name: paramName, in: 'path', required: true, type: 'string', description: `Path parameter: ${paramName}`, }); } // Add basic request and response parameters parameters.push({ name: 'req', in: 'body', required: true, type: 'object', description: 'Request object', }); parameters.push({ name: 'res', in: 'body', required: true, type: 'object', description: 'Response object', }); return parameters; } /** * Extract responses from function */ static extractResponses(func) { return [ { code: '200', description: 'Successful response', schema: { type: 'object', properties: { success: { type: 'boolean' }, data: { type: 'object' }, }, }, }, { code: '400', description: 'Bad request', schema: { type: 'object', properties: { success: { type: 'boolean' }, error: { type: 'string' }, }, }, }, { code: '500', description: 'Internal server error', schema: { type: 'object', properties: { success: { type: 'boolean' }, error: { type: 'string' }, }, }, }, ]; } } exports.ASTParser = ASTParser; //# sourceMappingURL=astParser.js.map