use crate::cli::*;
use crate::generator::ProjectConfig;
use std::path::PathBuf;
pub fn generate(
project_path: &PathBuf,
config: &ProjectConfig,
) -> Result<(), Box<dyn std::error::Error>> {
let deps = get_dependencies(config);
let dev_deps = get_dev_dependencies(config);
let scripts = get_scripts(config);
let package_json = format!(
r#"{{
"name": "{}",
"version": "1.0.0",
"description": "Generated by Acrocoder CLI ⚡",
"main": "dist/server.js",
"scripts": {{
{}
}},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {{
{}
}},
"devDependencies": {{
{}
}}
}}
"#,
config.name,
scripts
.iter()
.map(|(k, v)| format!(" \"{}\": \"{}\"", k, v.replace("\"", "\\\"")))
.collect::<Vec<_>>()
.join(",\n"),
deps.iter()
.map(|(k, v)| format!(" \"{}\": \"{}\"", k, v))
.collect::<Vec<_>>()
.join(",\n"),
dev_deps
.iter()
.map(|(k, v)| format!(" \"{}\": \"{}\"", k, v))
.collect::<Vec<_>>()
.join(",\n"),
);
std::fs::write(project_path.join("package.json"), package_json)?;
Ok(())
}
fn get_scripts(config: &ProjectConfig) -> Vec<(&str, String)> {
let mut scripts: Vec<(&str, String)> = vec![
("dev", "ts-node-dev --respawn --transpile-only src/server.ts".to_string()),
("build", "tsc".to_string()),
("start", "node dist/server.js".to_string()),
("lint", "eslint src/ --ext .ts".to_string()),
("lint:fix", "eslint src/ --ext .ts --fix".to_string()),
("format", "prettier --write \"src/**/*.ts\"".to_string()),
];
match config.test {
TestFramework::Jest => {
scripts.push(("test", "jest --passWithNoTests".to_string()));
scripts.push(("test:watch", "jest --watch".to_string()));
scripts.push(("test:coverage", "jest --coverage".to_string()));
}
TestFramework::Vitest => {
scripts.push(("test", "vitest run".to_string()));
scripts.push(("test:watch", "vitest".to_string()));
scripts.push(("test:coverage", "vitest --coverage".to_string()));
}
}
if config.db.is_some() && config.resolve_orm() == Some(Orm::Prisma) {
scripts.push(("prisma:generate", "prisma generate".to_string()));
scripts.push(("prisma:migrate", "prisma migrate dev".to_string()));
scripts.push(("prisma:studio", "prisma studio".to_string()));
}
scripts
}
fn get_dependencies(config: &ProjectConfig) -> Vec<(&str, &str)> {
let mut deps: Vec<(&str, &str)> = vec![
("dotenv", "^16.4.7"),
("cors", "^2.8.5"),
("helmet", "^8.0.0"),
("hpp", "^0.2.3"),
("compression", "^1.7.4"),
("express-rate-limit", "^7.5.0"),
("http-status-codes", "^2.3.0"),
];
match config.framework {
Framework::Express => {
deps.push(("express", "^4.21.2"));
}
Framework::Fastify => {
deps.push(("fastify", "^5.2.1"));
deps.push(("@fastify/cors", "^10.0.2"));
deps.push(("@fastify/helmet", "^13.0.1"));
deps.push(("@fastify/rate-limit", "^10.2.1"));
deps.push(("@fastify/swagger", "^9.4.2"));
deps.push(("@fastify/swagger-ui", "^5.2.2"));
}
Framework::Nest => {
deps.push(("@nestjs/core", "^10.4.15"));
deps.push(("@nestjs/common", "^10.4.15"));
deps.push(("@nestjs/platform-express", "^10.4.15"));
deps.push(("reflect-metadata", "^0.2.2"));
deps.push(("rxjs", "^7.8.1"));
}
}
match config.logger {
LoggerLib::Winston => {
deps.push(("winston", "^3.17.0"));
}
LoggerLib::Pino => {
deps.push(("pino", "^9.6.0"));
deps.push(("pino-pretty", "^13.0.0"));
}
}
match config.validation {
ValidationLib::Zod => {
deps.push(("zod", "^3.24.2"));
}
ValidationLib::Joi => {
deps.push(("joi", "^17.13.3"));
}
}
if let Some(db) = config.db {
match config.resolve_orm() {
Some(Orm::Mongoose) => {
deps.push(("mongoose", "^8.10.1"));
}
Some(Orm::Prisma) => {
deps.push(("@prisma/client", "^6.4.1"));
}
Some(Orm::Sequelize) => {
deps.push(("sequelize", "^6.37.5"));
match db {
Database::Postgres => deps.push(("pg", "^8.13.1")),
Database::Mysql => deps.push(("mysql2", "^3.12.0")),
Database::Sqlite => deps.push(("sqlite3", "^5.1.7")),
_ => {}
}
}
Some(Orm::Typeorm) => {
deps.push(("typeorm", "^0.3.20"));
match db {
Database::Postgres => deps.push(("pg", "^8.13.1")),
Database::Mysql => deps.push(("mysql2", "^3.12.0")),
Database::Sqlite => deps.push(("better-sqlite3", "^11.8.1")),
_ => {}
}
}
None => {}
}
}
if let Some(auth) = config.auth {
match auth {
AuthStrategy::Jwt => {
deps.push(("jsonwebtoken", "^9.0.2"));
deps.push(("bcryptjs", "^2.4.3"));
}
AuthStrategy::Session => {
deps.push(("express-session", "^1.18.1"));
deps.push(("connect-redis", "^8.0.1"));
deps.push(("bcryptjs", "^2.4.3"));
}
AuthStrategy::Oauth => {
deps.push(("passport", "^0.7.0"));
deps.push(("passport-google-oauth20", "^2.0.0"));
deps.push(("passport-github2", "^0.1.12"));
deps.push(("jsonwebtoken", "^9.0.2"));
}
AuthStrategy::Firebase => {
deps.push(("firebase-admin", "^13.0.2"));
}
}
}
if config.framework == Framework::Express {
deps.push(("swagger-jsdoc", "^6.2.8"));
deps.push(("swagger-ui-express", "^5.0.1"));
}
deps
}
fn get_dev_dependencies(config: &ProjectConfig) -> Vec<(&str, &str)> {
let mut dev_deps: Vec<(&str, &str)> = vec![
("typescript", "^5.7.3"),
("ts-node-dev", "^2.0.0"),
("@types/node", "^22.13.4"),
("eslint", "^9.20.0"),
("@typescript-eslint/parser", "^8.24.1"),
("@typescript-eslint/eslint-plugin", "^8.24.1"),
("prettier", "^3.5.2"),
];
match config.framework {
Framework::Express => {
dev_deps.push(("@types/express", "^5.0.0"));
dev_deps.push(("@types/cors", "^2.8.17"));
dev_deps.push(("@types/compression", "^1.7.5"));
dev_deps.push(("@types/hpp", "^0.2.6"));
dev_deps.push(("@types/swagger-jsdoc", "^6.0.4"));
dev_deps.push(("@types/swagger-ui-express", "^4.1.7"));
}
_ => {}
}
match config.test {
TestFramework::Jest => {
dev_deps.push(("jest", "^29.7.0"));
dev_deps.push(("ts-jest", "^29.2.5"));
dev_deps.push(("@types/jest", "^29.5.14"));
}
TestFramework::Vitest => {
dev_deps.push(("vitest", "^3.0.5"));
}
}
if config.resolve_orm() == Some(Orm::Prisma) {
dev_deps.push(("prisma", "^6.4.1"));
}
if let Some(auth) = config.auth {
match auth {
AuthStrategy::Jwt => {
dev_deps.push(("@types/jsonwebtoken", "^9.0.7"));
dev_deps.push(("@types/bcryptjs", "^2.4.6"));
}
AuthStrategy::Session => {
dev_deps.push(("@types/express-session", "^1.18.1"));
dev_deps.push(("@types/bcryptjs", "^2.4.6"));
}
AuthStrategy::Oauth => {
dev_deps.push(("@types/passport", "^1.0.17"));
dev_deps.push(("@types/passport-google-oauth20", "^2.0.16"));
}
_ => {}
}
}
dev_deps
}