4bnode
Version:
A professional tool to generate a Node.js app by 4Brains Technologies
236 lines (209 loc) • 7.35 kB
JavaScript
import fs from "fs";
import path from "path";
import readline from "readline";
import chalk from "chalk";
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
function listRoutes() {
const projectRoot = process.cwd();
const routesDir = path.join(projectRoot, "src", "routes");
if (!fs.existsSync(routesDir)) {
console.error(chalk.red("No routes directory found in src/routes"));
return [];
}
return fs.readdirSync(routesDir).filter((file) => file.endsWith(".js"));
}
function listModels() {
const projectRoot = process.cwd();
const modelsDir = path.join(projectRoot, "src", "models");
if (!fs.existsSync(modelsDir)) {
console.error(chalk.red("No models directory found in src/models"));
return [];
}
return fs.readdirSync(modelsDir).filter((file) => file.endsWith(".js"));
}
function getModelSchema(modelName) {
const projectRoot = process.cwd();
const modelFilePath = path.join(
projectRoot,
"src",
"models",
modelName + ".js"
);
if (!fs.existsSync(modelFilePath)) {
console.error(chalk.red(`Model file not found: ${modelFilePath}`));
return [];
}
const modelFileContent = fs.readFileSync(modelFilePath, "utf8");
const schemaRegex = /new mongoose\.Schema\s*\(\s*\{([\s\S]*?)\}\s*\)/m;
const match = modelFileContent.match(schemaRegex);
if (match) {
try {
const schemaContent = match[1];
const fieldRegex = /(\w+):\s*\{[\s\S]*?\}/g;
const schemaKeys = ["_id"]; // Always include `_id`
let fieldMatch;
while ((fieldMatch = fieldRegex.exec(schemaContent)) !== null) {
schemaKeys.push(fieldMatch[1]);
}
return schemaKeys;
} catch (error) {
console.error(chalk.red("Error parsing schema: "), error);
return ["_id"];
}
} else {
console.error(chalk.red("Schema not found in model file."));
return ["_id"];
}
}
function addReadTemplateToRoute(routeFile, modelName, selectedField = null) {
const projectRoot = process.cwd();
const routeFilePath = path.join(projectRoot, "src", "routes", routeFile);
const pascalModelName =
modelName.charAt(0).toUpperCase() + modelName.slice(1);
const readTemplateImport = `import ${pascalModelName} from '../models/${modelName}.js';\n`;
let readTemplateFunction;
if (selectedField) {
readTemplateFunction = `
// Fetch data based on ${selectedField}
try {
const documents = await ${pascalModelName}.find({ ${selectedField}: req.query.${selectedField} });
if (documents.length === 0) {
return res.status(404).json({ message: "No matching documents found" });
}
res.json({ data: documents });
} catch (err) {
console.error(err);
res.status(500).json({ message: "Server error" });
}
`;
} else {
readTemplateFunction = `
// Fetch all data
try {
const documents = await ${pascalModelName}.find();
res.json({ data: documents });
} catch (err) {
console.error(err);
res.status(500).json({ message: "Server error" });
}
`;
}
if (!fs.existsSync(routeFilePath)) {
console.error(
chalk.red(`The specified route file does not exist: ${routeFilePath}`)
);
return;
}
let routeContent = fs.readFileSync(routeFilePath, "utf8");
if (!routeContent.includes(`import ${pascalModelName}`)) {
routeContent = readTemplateImport + routeContent;
}
const lastCloseIndex = routeContent.lastIndexOf("});");
if (lastCloseIndex !== -1) {
const updatedContent = [
routeContent.slice(0, lastCloseIndex),
readTemplateFunction,
routeContent.slice(lastCloseIndex),
].join("\n");
routeContent = updatedContent;
} else {
routeContent += `\n${readTemplateFunction}\n`;
}
fs.writeFileSync(routeFilePath, routeContent, "utf8");
console.log(chalk.green(`Read template added to ${routeFile}`));
}
// Main Logic
const routeFiles = listRoutes();
const modelFiles = listModels();
if (routeFiles.length === 0 || modelFiles.length === 0) {
rl.close();
process.exit(0);
}
console.log(chalk.blue("Select the route file to modify:"));
routeFiles.forEach((file, index) => {
console.log(chalk.yellow(`${index + 1}. ${file}`));
});
rl.question(
chalk.cyan("Enter the number of the route file: "),
(routeAnswer) => {
const routeIndex = parseInt(routeAnswer, 10) - 1;
if (routeIndex >= 0 && routeIndex < routeFiles.length) {
const selectedRoute = routeFiles[routeIndex];
console.log(chalk.blue("Select the model file to use:"));
modelFiles.forEach((file, index) => {
console.log(chalk.yellow(`${index + 1}. ${file}`));
});
rl.question(
chalk.cyan("Enter the number of the model file: "),
(modelAnswer) => {
const modelIndex = parseInt(modelAnswer, 10) - 1;
if (modelIndex >= 0 && modelIndex < modelFiles.length) {
const selectedModel = path.basename(modelFiles[modelIndex], ".js");
const schemaFields = getModelSchema(selectedModel);
console.log(chalk.green("Do you want to:"));
console.log(chalk.green("1. Fetch all data"));
console.log(chalk.green("2. Fetch data based on a specific field"));
rl.question(
chalk.cyan("Enter your choice (1 or 2): "),
(choiceAnswer) => {
if (choiceAnswer === "1") {
addReadTemplateToRoute(selectedRoute, selectedModel);
rl.close();
} else if (choiceAnswer === "2" && schemaFields.length > 0) {
console.log(
chalk.green(
"Select a field to filter data by (_id is always available):"
)
);
schemaFields.forEach((field, index) => {
console.log(chalk.yellow(`${index + 1}. ${field}`));
});
rl.question(
chalk.cyan("Enter the number of the field: "),
(fieldAnswer) => {
const fieldIndex = parseInt(fieldAnswer, 10) - 1;
if (fieldIndex >= 0 && fieldIndex < schemaFields.length) {
const selectedField = schemaFields[fieldIndex];
addReadTemplateToRoute(
selectedRoute,
selectedModel,
selectedField
);
} else {
console.error(
chalk.red(
"Invalid selection. Please choose a valid field number."
)
);
}
rl.close();
}
);
} else {
console.error(
chalk.red("Invalid choice. Please enter 1 or 2.")
);
rl.close();
}
}
);
} else {
console.error(
chalk.red("Invalid selection. Please choose a valid number.")
);
rl.close();
}
}
);
} else {
console.error(
chalk.red("Invalid selection. Please choose a valid number.")
);
rl.close();
}
}
);