UNPKG

4bnode

Version:

4bnode is a CLI-powered backend development platform with a built-in visual dashboard to generate, manage, and test Node.js/Express APIs faster.

127 lines (105 loc) 3.58 kB
#!/usr/bin/env node import path from "path"; import { select, checkbox } from "@inquirer/prompts"; import { toPascalCase } from "./lib/names.js"; import { listRoutes, listModels, getModelSchema } from "./lib/project.js"; import { getRouteFilePath, addImportToRoute, insertCodeIntoRoute, detectHttpMethod, getRequestDataSource, } from "./lib/routeFile.js"; import { showHeader, showTaskDone, showError, showFileAction, installDeps, } from "./lib/ui.js"; import { buildInsertCode, buildReadCode, buildUpdateCode, buildDeleteCode, } from "./lib/codegen.js"; async function main() { showHeader("add-crud", "Add CRUD operations to a route"); const routeFiles = listRoutes(); if (routeFiles.length === 0) { showError("No route files found in src/routes."); process.exit(1); } const modelFiles = listModels(); if (modelFiles.length === 0) { showError("No model files found in src/models."); process.exit(1); } const selectedRoute = await select({ message: "Select route file:", choices: routeFiles.map((f) => ({ name: f, value: f })), }); const selectedModelFile = await select({ message: "Select model file:", choices: modelFiles.map((f) => ({ name: f, value: f })), }); const operations = await checkbox({ message: "Select CRUD operations:", choices: [ { name: "Create (POST /create)", value: "create", checked: true }, { name: "Read (GET / and GET /:id)", value: "read", checked: true }, { name: "Update (PUT /:id)", value: "update", checked: true }, { name: "Delete (DELETE /:id)", value: "delete", checked: true }, ], validate: (v) => (v.length > 0 ? true : "Select at least one operation."), }); const modelName = path.basename(selectedModelFile, ".js"); const pascalName = toPascalCase(modelName); const fields = getModelSchema(modelName); if (fields.length === 0) { showError("No fields found in the selected model."); process.exit(1); } console.log(); const routeFilePath = getRouteFilePath(selectedRoute); const method = detectHttpMethod(routeFilePath); const dataSource = getRequestDataSource(method); const needsBcrypt = fields.includes("password") && (operations.includes("create") || operations.includes("update")); // Build import let importBlock = `import ${pascalName} from '../models/${modelName}.js';`; if (needsBcrypt) { importBlock = `import bcrypt from 'bcrypt';\n${importBlock}`; } addImportToRoute(routeFilePath, importBlock); // Add selected operations const added = []; if (operations.includes("create")) { insertCodeIntoRoute(routeFilePath, buildInsertCode(pascalName, fields, dataSource)); added.push("POST /create"); } if (operations.includes("read")) { insertCodeIntoRoute(routeFilePath, buildReadCode(pascalName)); added.push("GET / and GET /:id"); } if (operations.includes("update")) { insertCodeIntoRoute(routeFilePath, buildUpdateCode(pascalName, fields, dataSource)); added.push("PUT /:id"); } if (operations.includes("delete")) { insertCodeIntoRoute(routeFilePath, buildDeleteCode(pascalName)); added.push("DELETE /:id"); } showFileAction("updated", `src/routes/${selectedRoute}`); if (needsBcrypt) { await installDeps("bcrypt"); } showTaskDone(`CRUD operations added to ${selectedRoute}`, [ `Model: ${modelName}`, ...added.map((a) => `Route: ${a}`), ]); } main().catch((err) => { if (err.name === "ExitPromptError") process.exit(0); showError(err.message); process.exit(1); });