create-lamdera-app-rs 0.1.8

A CLI tool to scaffold Lamdera applications with Tailwind CSS, authentication, i18n, and testing
Documentation
use clap::Parser;
use colored::*;
use create_lamdera_app_rs::*;
use std::env;
use std::fs;
use std::path::PathBuf;

fn main() -> Result<()> {
    let args = args::Args::parse();

    // Validate arguments
    if let Err(e) = args.validate() {
        eprintln!("{}", format!("{}", e).red());
        std::process::exit(1);
    }

    // Handle special modes
    if args.init {
        return handle_init_mode();
    }

    if args.install_precommit {
        return handle_precommit_install();
    }

    // Main project creation flow
    create_project(args)
}

fn create_project(args: args::Args) -> Result<()> {
    let is_interactive = args.name.is_none();

    // Interactive mode if name or github not provided
    let project_name = if let Some(ref name) = args.name {
        name.clone()
    } else {
        interactive::prompt_project_name()?
    };

    let create_github = if let Some(create) = args.should_create_github() {
        create
    } else {
        interactive::prompt_create_github()?
    };

    let is_public = if create_github && !args.public {
        interactive::prompt_repository_visibility()?
    } else {
        args.public
    };

    let package_manager = if is_interactive {
        interactive::prompt_package_manager()?
    } else {
        args.get_package_manager()
    };

    let simple = if is_interactive {
        interactive::prompt_boilerplate_type()?
    } else {
        args.simple
    };

    // Check if directory exists
    let project_path = PathBuf::from(&project_name);
    if project_path.exists() {
        eprintln!(
            "{}",
            format!("❌ Directory '{}' already exists", project_name).red()
        );
        std::process::exit(1);
    }

    // Create project directory
    fs::create_dir_all(&project_path)?;
    env::set_current_dir(&project_path)?;

    // Now we're inside the project directory, so use current dir (.)
    let current_dir = PathBuf::from(".");

    // Copy boilerplate
    templates::copy_boilerplate(&current_dir, simple)?;

    // Update package.json for package manager
    templates::update_package_json(&current_dir, package_manager.as_str())?;

    // Initialize git
    git::init_repository(&current_dir)?;
    git::make_hooks_executable(&current_dir)?;

    // Install dependencies
    if !args.skip_install {
        package_manager::install_dependencies(&current_dir, &package_manager)?;
    }

    // Create initial commit
    git::initial_commit(&current_dir)?;

    // Enable hooks after the initial scaffold commit so project creation is not
    // blocked by tools the user has not installed yet.
    git::configure_hooks(&current_dir)?;

    // Create GitHub repository if requested
    if create_github {
        if !github::is_gh_cli_installed() {
            eprintln!("{}", "⚠️  GitHub CLI (gh) not installed. Skipping repository creation.".yellow());
            eprintln!("{}", "   Install it from: https://cli.github.com/".yellow());
        } else if !github::is_gh_cli_authenticated() {
            eprintln!("{}", "⚠️  GitHub CLI not authenticated. Run 'gh auth login' first.".yellow());
        } else {
            match github::create_repository(&project_name, is_public) {
                Ok(_) => {
                    git::push_to_remote(&current_dir)?;
                }
                Err(e) => {
                    eprintln!("{}", format!("⚠️  Failed to create GitHub repository: {}", e).yellow());
                }
            }
        }
    }

    print_success_message(&project_name, &package_manager, simple);

    Ok(())
}

fn handle_init_mode() -> Result<()> {
    println!("{}", "🚧 Init mode not yet implemented".yellow());
    Ok(())
}

fn handle_precommit_install() -> Result<()> {
    let current_dir = env::current_dir()?;
    git::configure_hooks(&current_dir)?;
    println!("{}", "✓ Pre-commit hooks installed".green());
    Ok(())
}

fn print_success_message(project_name: &str, package_manager: &args::PackageManager, simple: bool) {
    println!("\n{}", "✨ Project setup complete!".green().bold());

    let pm = package_manager.as_str();
    let run_script = if pm == "npm" { "npm run" } else { "bun run" };

    println!("\n📦 {}", "To start development:".bold());
    println!("  cd {}", project_name);
    println!("  {} install (install dependencies first)", pm);
    println!("  {} start (runs Lamdera + Tailwind watcher)", pm);

    println!("\n🔧 {}", "Development commands:".bold());
    println!("  PORT=3000 {} start (custom port)", pm);
    println!("  {} run start:hot (with elm-pkg-js hot-reload)", pm);

    println!("\n🧪 {}", "Testing:".bold());
    println!("  {} test", run_script);
    println!("  {} check", run_script);
    println!("  Check tests/Tests.elm for lamdera-program-test examples");

    println!("\n🌐 {}", "Features included:".bold());
    println!("  • Tailwind CSS + Dark mode");
    println!("  • i18n (EN/FR) with localStorage");
    println!("  • Authentication (Google, GitHub, Email)");
    println!("  • lamdera-program-test integration");
    println!("  • Development config panel (bottom-left in dev mode)");
    println!("  • Pre-commit hooks with elm-review");

    if !simple {
        println!("  • Counter demo (increment/decrement)");
        println!("  • Chat system (real-time messaging)");
    }

    println!("\n📚 {}", "Documentation:".bold());
    println!("  • HOW_TO_WRITE_TESTS.md");
    println!("  • GOOGLE_ONE_TAP_SETUP.md");
    println!("  • GITHUB_OAUTH_SETUP.md");
    println!("  • ENVIRONMENT_SETUP.md (set passwordSecret before production)");
}