UNPKG

quickwire

Version:

Automatic API generator for Next.js applications that creates API routes and TypeScript client functions from backend functions

225 lines 8.72 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.analyzeModuleExports = analyzeModuleExports; exports.hasExportModifier = hasExportModifier; exports.analyzeFunctionDeclaration = analyzeFunctionDeclaration; exports.analyzeVariableStatement = analyzeVariableStatement; exports.analyzeTypeDeclaration = analyzeTypeDeclaration; exports.detectHttpMethod = detectHttpMethod; exports.detectContextUsage = detectContextUsage; const fs_1 = __importDefault(require("fs")); const typescript_1 = __importDefault(require("typescript")); const config_1 = require("./config"); const cache_1 = require("./cache"); function analyzeModuleExports(filePath) { // Check cache first const cached = (0, cache_1.getCachedExports)(filePath); if (cached) return cached; let content; try { content = fs_1.default.readFileSync(filePath, "utf-8"); } catch (error) { console.warn(`⚠️ Failed to read file ${filePath}:`, error); return { functions: [], types: [], imports: [] }; } const sourceFile = typescript_1.default.createSourceFile(filePath, content, config_1.CONFIG.compilerOptions.target, true, typescript_1.default.ScriptKind.TS); const functions = []; const types = []; const imports = []; function visit(node) { try { if (typescript_1.default.isImportDeclaration(node)) { imports.push(node.getText().trim()); } if (hasExportModifier(node)) { if (typescript_1.default.isFunctionDeclaration(node) && node.name) { const func = analyzeFunctionDeclaration(node); func.httpMethod = detectHttpMethod(func.name, func.parameters); functions.push(func); } else if (typescript_1.default.isVariableStatement(node)) { const funcs = analyzeVariableStatement(node); funcs.forEach(func => { func.httpMethod = detectHttpMethod(func.name, func.parameters); }); functions.push(...funcs); } else { const exportedType = analyzeTypeDeclaration(node); if (exportedType) types.push(exportedType); } } typescript_1.default.forEachChild(node, visit); } catch (error) { console.warn(`⚠️ Error analyzing node in ${filePath}:`, error); } } visit(sourceFile); const result = { functions, types, imports }; (0, cache_1.setCachedExports)(filePath, result); return result; } function hasExportModifier(node) { return ((typescript_1.default.canHaveModifiers(node) && typescript_1.default .getModifiers(node) ?.some((m) => m.kind === typescript_1.default.SyntaxKind.ExportKeyword)) || false); } function analyzeFunctionDeclaration(node) { const name = node.name.text; const parameters = node.parameters.map((p) => ({ name: p.name.text, type: p.type?.getText() || "any", optional: !!p.questionToken, })); const returnType = node.type?.getText(); const isAsync = node.modifiers?.some((m) => m.kind === typescript_1.default.SyntaxKind.AsyncKeyword) || false; // Check if function needs request context const needsContext = detectContextUsage(node, parameters); return { name, type: "function", parameters, returnType, isAsync, needsContext, node }; } function analyzeVariableStatement(node) { const funcs = []; for (const decl of node.declarationList.declarations) { if (typescript_1.default.isIdentifier(decl.name) && decl.initializer) { let funcNode = null; let type = "variable"; if (typescript_1.default.isArrowFunction(decl.initializer)) { funcNode = decl.initializer; type = "arrow"; } else if (typescript_1.default.isFunctionExpression(decl.initializer)) { funcNode = decl.initializer; type = "variable"; } if (funcNode) { const parameters = funcNode.parameters.map((p) => ({ name: p.name.text, type: p.type?.getText() || "any", optional: !!p.questionToken, })); const returnType = funcNode.type?.getText(); const isAsync = funcNode.modifiers?.some((m) => m.kind === typescript_1.default.SyntaxKind.AsyncKeyword) || false; // Check if function needs request context const needsContext = detectContextUsage(funcNode, parameters); funcs.push({ name: decl.name.text, type, parameters, returnType, isAsync, needsContext, node: decl, }); } } } return funcs; } function analyzeTypeDeclaration(node) { try { if (typescript_1.default.isInterfaceDeclaration(node)) { return { name: node.name.text, type: "interface", declaration: node.getText().trim(), node, }; } if (typescript_1.default.isTypeAliasDeclaration(node)) { return { name: node.name.text, type: "type", declaration: node.getText().trim(), node, }; } if (typescript_1.default.isEnumDeclaration(node)) { return { name: node.name.text, type: "enum", declaration: node.getText().trim(), node, }; } if (typescript_1.default.isClassDeclaration(node) && node.name) { return { name: node.name.text, type: "class", declaration: node.getText().trim(), node, }; } } catch (error) { console.warn(`⚠️ Error analyzing type declaration:`, error); } return null; } function detectHttpMethod(functionName, parameters) { const lowerName = functionName.toLowerCase(); // First check function name for HTTP method detection for (const [method, prefixes] of Object.entries(config_1.CONFIG.httpMethods)) { for (const prefix of prefixes) { if (lowerName.startsWith(prefix)) { return method; } } } // If no method matches, default to POST return 'POST'; } // New function to detect if a function needs request context function detectContextUsage(node, parameters) { // Check if any parameter is of type QuickwireContext or contains context-related keywords const hasContextParam = parameters.some(param => { const type = param.type.toLowerCase(); return (type.includes('quickwirecontext') || type.includes('context') || type.includes('request') || type.includes('req') || param.name.toLowerCase().includes('context') || param.name.toLowerCase().includes('req')); }); if (hasContextParam) return true; // Check function body for usage of context-related APIs let needsContext = false; function visitNode(node) { // Look for property access that suggests context usage if (typescript_1.default.isPropertyAccessExpression(node)) { const text = node.getText(); if (text.includes('headers') || text.includes('cookies') || text.includes('userAgent') || text.includes('ip') || text.includes('req.') || text.includes('request.')) { needsContext = true; } } // Look for calls to context-related functions if (typescript_1.default.isCallExpression(node)) { const text = node.getText(); if (text.includes('getHeader') || text.includes('getCookie') || text.includes('getClientIP')) { needsContext = true; } } if (!needsContext) { typescript_1.default.forEachChild(node, visitNode); } } visitNode(node); return needsContext; } //# sourceMappingURL=ast.js.map