UNPKG

okai

Version:

AI-powered code generation tool for ServiceStack Apps. Generate TypeScript data models, C# APIs, migrations, and UI components from natural language prompts using LLMs.

170 lines 5.62 kB
import fs from "fs"; import path from "path"; export function isDir(filePath) { try { return fs.statSync(filePath).isDirectory(); } catch (e) { return false; } } export function projectInfo(cwd) { const config = fs.existsSync(path.join(cwd, "okai.json")) ? JSON.parse(fs.readFileSync(path.join(cwd, "okai.json")).toString()) : null; if (config) return config; // Search for .sln or .slnx file by walking up the directory tree let slnDir = ''; let sln = ''; let currentDir = cwd; const root = path.parse(currentDir).root; while (currentDir !== root) { try { const found = fs.readdirSync(currentDir).find(f => f.endsWith(".sln") || f.endsWith(".slnx")); if (found) { sln = found; slnDir = currentDir; break; } } catch (e) { // ignore permission errors } const parentDir = path.dirname(currentDir); if (parentDir === currentDir) break; // reached root currentDir = parentDir; } if (!sln) { return null; } const projectName = sln.endsWith('.slnx') ? sln.substring(0, sln.length - 5) : sln.substring(0, sln.length - 4); function getDir(slnDir, match) { if (fs.readdirSync(slnDir).find(match)) return slnDir; const dirs = fs.readdirSync(slnDir).filter(f => isDir(path.join(slnDir, f))); for (let dir of dirs) { try { const hasFile = fs.readdirSync(path.join(slnDir, dir)).find(match); if (hasFile) return path.join(slnDir, dir); } catch (e) { // ignore } } return null; } const hostDir = getDir(slnDir, f => f === `${projectName}.csproj`); if (!hostDir) { return null; } const serviceModelDirName = fs.readdirSync(slnDir).find(f => f.endsWith("ServiceModel")); const serviceModelDir = serviceModelDirName ? path.join(slnDir, serviceModelDirName) : null; const serviceInterfaceDirName = fs.readdirSync(slnDir).find(f => f.endsWith("ServiceInterface")); const serviceInterfaceDir = serviceInterfaceDirName ? path.join(slnDir, serviceInterfaceDirName) : null; const migrationsDir = hostDir && fs.readdirSync(hostDir).find(f => f === "Migrations") ? path.join(hostDir, "Migrations") : null; const info = { projectName, slnDir, hostDir, migrationsDir, serviceModelDir, serviceInterfaceDir, }; const uiVueDir = path.join(hostDir, "wwwroot", "admin", "sections"); if (fs.existsSync(uiVueDir)) { info.uiMjsDir = uiVueDir; } for (const file of walk(serviceInterfaceDir, [], { include: (path) => path.endsWith(".cs"), excludeDirs: ["obj", "bin"] })) { const content = fs.readFileSync(file).toString(); const userInfo = parseUserType(content); if (userInfo) { info.userType = userInfo.userType; info.userIdType = userInfo.userIdType ?? 'string'; break; } } for (const file of walk(serviceModelDir, [], { include: (path) => path.endsWith(".cs"), excludeDirs: ["obj", "bin"] })) { const content = fs.readFileSync(file).toString(); const dtoInfo = parseUserDtoType(content); if (dtoInfo?.userType) { info.userType = dtoInfo.userType; if (dtoInfo.userIdType) info.userIdType = dtoInfo.userIdType; if (dtoInfo.userLabel) info.userLabel = dtoInfo.userLabel; break; } } return config ? Object.assign({}, info, config) : info; } export function parseUserType(cs) { const typePattern = /class\s+(\w+)\s*:\s*IdentityUser(?:<(.+)>)?/; const match = cs.match(typePattern); if (!match) return null; return { userType: match[1], // Type name userIdType: match[2] || null // Generic arguments (content between < >) }; } export function parseUserDtoType(cs) { const userAliasPos = cs.indexOf('[Alias("AspNetUsers")]'); if (userAliasPos === -1) return null; const typeMatch = cs.substring(userAliasPos).match(/class\s+(\w+)\s*/); if (!typeMatch) return null; const idMatch = cs.substring(userAliasPos).match(/\s+(\w+\??)\s+Id\s+/i); const nameMatch = Array.from(cs.substring(userAliasPos).matchAll(/\s+(\w+Name)\s+/ig)) .find(x => x[1].toLowerCase() !== 'username')?.[1]; const userLabel = cs.includes('DisplayName') ? 'DisplayName' : nameMatch ? nameMatch : undefined; return { userType: typeMatch[1], // DTO Type name userIdType: idMatch ? idMatch[1] : null, userLabel, }; } function walk(dir, fileList = [], opt) { if (!dir) return fileList; const files = fs.readdirSync(dir); for (const file of files) { const filePath = path.join(dir, file); const stat = fs.statSync(filePath); if (stat.isDirectory()) { if (!opt?.excludeDirs || !opt.excludeDirs.includes(file)) { walk(filePath, fileList, opt); } } else { if (!opt?.include || opt?.include(filePath)) { fileList.push(filePath); } } } return fileList; } //# sourceMappingURL=info.js.map