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
JavaScript
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
;