node-project-gen 0.0.4

⚡ Blazing fast, architecture-aware Node.js backend generator. Scaffold production-ready projects in under 1 second.
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>> {
    match config.framework {
        Framework::Express => generate_express_server(project_path, config)?,
        Framework::Fastify => generate_fastify_server(project_path, config)?,
        Framework::Nest => generate_nest_server(project_path, config)?,
    }
    Ok(())
}

fn generate_express_server(
    project_path: &PathBuf,
    config: &ProjectConfig,
) -> Result<(), Box<dyn std::error::Error>> {
    let app_import = config.get_app_import_path();
    let config_import = config.get_env_config_import_path_from_src();
    let logger_import = config.get_logger_import_path_from_src();

    let content = format!(
        r#"import app from '{app_import}';
import {{ config }} from '{config_import}';
import {{ logger }} from '{logger_import}';

const startServer = async (): Promise<void> => {{
  try {{
    await app.initialize();

    app.app.listen(config.port, () => {{
      logger.info(`🚀 Server running on port ${{config.port}}`);
      logger.info(`📖 API Docs: http://localhost:${{config.port}}/api-docs`);
      logger.info(`🏥 Health: http://localhost:${{config.port}}/health`);
      logger.info(`🌍 Environment: ${{config.nodeEnv}}`);
    }});
  }} catch (error) {{
    logger.error('Failed to start server:', error);
    process.exit(1);
  }}
}};

// Graceful shutdown
const gracefulShutdown = (signal: string): void => {{
  logger.info(`${{signal}} received. Starting graceful shutdown...`);
  process.exit(0);
}};

process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
process.on('uncaughtException', (error) => {{
  logger.error('Uncaught Exception:', error);
  process.exit(1);
}});
process.on('unhandledRejection', (reason) => {{
  logger.error('Unhandled Rejection:', reason);
  process.exit(1);
}});

startServer();
"#,
        app_import = app_import,
        config_import = config_import,
        logger_import = logger_import
    );

    std::fs::write(
        project_path.join(format!("src/server.{}", config.get_ext())),
        content,
    )?;

    Ok(())
}

fn generate_fastify_server(
    project_path: &PathBuf,
    config: &ProjectConfig,
) -> Result<(), Box<dyn std::error::Error>> {
    let app_import = config.get_app_import_path();
    let config_import = config.get_env_config_import_path_from_src();
    let logger_import = config.get_logger_import_path_from_src();

    let content = format!(
        r#"import app from '{app_import}';
import {{ config }} from '{config_import}';
import {{ logger }} from '{logger_import}';

const startServer = async (): Promise<void> => {{
  try {{
    await app.initialize();

    await app.app.listen({{ port: config.port, host: '0.0.0.0' }});

    logger.info(`🚀 Fastify server running on port ${{config.port}}`);
    logger.info(`📖 API Docs: http://localhost:${{config.port}}/api-docs`);
    logger.info(`🏥 Health: http://localhost:${{config.port}}/health`);
    logger.info(`🌍 Environment: ${{config.nodeEnv}}`);
  }} catch (error) {{
    logger.error('Failed to start server:', error);
    process.exit(1);
  }}
}};

// Graceful shutdown
const gracefulShutdown = async (signal: string): Promise<void> => {{
  logger.info(`${{signal}} received. Starting graceful shutdown...`);
  await app.app.close();
  process.exit(0);
}};

process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
process.on('SIGINT', () => gracefulShutdown('SIGINT'));

startServer();
"#,
        app_import = app_import,
        config_import = config_import,
        logger_import = logger_import
    );

    std::fs::write(
        project_path.join(format!("src/server.{}", config.get_ext())),
        content,
    )?;

    Ok(())
}

fn generate_nest_server(
    project_path: &PathBuf,
    config: &ProjectConfig,
) -> Result<(), Box<dyn std::error::Error>> {
    let app_module_import = "./app.module";
    let config_import = config.get_env_config_import_path_from_src();
    let logger_import = config.get_logger_import_path_from_src();

    let content = format!(
        r#"import {{ NestFactory }} from '@nestjs/core';
import {{ AppModule }} from '{app_module_import}';
import {{ config }} from '{config_import}';
import {{ logger }} from '{logger_import}';

async function bootstrap(): Promise<void> {{
  const app = await NestFactory.create(AppModule);

  app.enableCors({{
    origin: config.corsOrigin,
    credentials: true,
  }});

  app.setGlobalPrefix('api/v1');

  await app.listen(config.port);

  logger.info(`🚀 NestJS server running on port ${{config.port}}`);
  logger.info(`🌍 Environment: ${{config.nodeEnv}}`);
}}

bootstrap();
"#,
        app_module_import = app_module_import,
        config_import = config_import,
        logger_import = logger_import
    );

    std::fs::write(
        project_path.join(format!("src/server.{}", config.get_ext())),
        content,
    )?;

    Ok(())
}