morph-cli 0.1.0

AST-based codebase migration and codemod tool for JavaScript and TypeScript projects.
Documentation
use std::fs;
use std::path::Path;
use anyhow::Result;
use clap::CommandFactory;
use crate::cli::Cli;
use crate::core::registry::RecipeRegistry;
use crate::core::presets::all_presets;
use crate::utils::terminal;

pub fn execute() -> Result<()> {
    let output_dir = Path::new("docs/generated");
    fs::create_dir_all(output_dir)?;

    println!("{} Generating documentation...", terminal::info_prefix());

    generate_commands_docs(output_dir)?;
    generate_recipes_docs(output_dir)?;
    generate_presets_docs(output_dir)?;
    generate_config_docs(output_dir)?;

    println!("{} Documentation generated in {}", terminal::success_prefix(), output_dir.display());

    Ok(())
}

fn generate_commands_docs(dir: &Path) -> Result<()> {
    let mut md = String::from("# morph-cli Commands\n\n");
    let cmd = Cli::command();

    for sub in cmd.get_subcommands() {
        md.push_str(&format!("## `morph {}`\n\n", sub.get_name()));
        if let Some(about) = sub.get_about() {
            md.push_str(&format!("{}\n\n", about));
        }

        let args: Vec<_> = sub.get_arguments().collect();
        if !args.is_empty() {
            md.push_str("### Arguments\n\n");
            md.push_str("| Argument | Description | Default |\n");
            md.push_str("| --- | --- | --- |\n");
            for arg in args {
                let name = arg.get_id();
                let help = arg.get_help().map(|h| h.to_string()).unwrap_or_default();
                let default = arg.get_default_values().iter().map(|v| v.to_string_lossy()).collect::<Vec<_>>().join(", ");
                md.push_str(&format!("| `{}` | {} | {} |\n", name, help, default));
            }
            md.push_str("\n");
        }
    }

    fs::write(dir.join("commands.md"), md)?;
    Ok(())
}

fn generate_recipes_docs(dir: &Path) -> Result<()> {
    let mut md = String::from("# Migration Recipes\n\n");
    let registry = RecipeRegistry::new();
    let recipes = registry.all();

    for recipe in recipes {
        let meta = recipe.metadata();
        md.push_str(&format!("## `{}`\n\n", meta.name));
        md.push_str(&format!("{}\n\n", meta.description));
        md.push_str(&format!("**Extensions:** {}\n\n", meta.supported_extensions.join(", ")));
        
        if !meta.required_recipes.is_empty() {
            md.push_str(&format!("**Depends on:** {}\n\n", meta.required_recipes.join(", ")));
        }
    }

    fs::write(dir.join("recipes.md"), md)?;
    Ok(())
}

fn generate_presets_docs(dir: &Path) -> Result<()> {
    let mut md = String::from("# Modernization Presets\n\n");
    let presets = all_presets();

    for preset in presets {
        md.push_str(&format!("## `{}`\n\n", preset.name));
        md.push_str(&format!("{}\n\n", preset.description));
        md.push_str(&format!("**Recipes:** {}\n\n", preset.recipes.join("")));
        md.push_str(&format!("**Risk Level:** {:?}\n\n", preset.risk_level));
    }

    fs::write(dir.join("presets.md"), md)?;
    Ok(())
}

fn generate_config_docs(dir: &Path) -> Result<()> {
    let mut md = String::from("# Configuration Options\n\n");
    md.push_str("Configure morph-cli by creating a `morph-cli.toml` file in your project root.\n\n");
    
    md.push_str("| Option | Type | Description | Default |\n");
    md.push_str("| --- | --- | --- | --- |\n");
    md.push_str("| `enabled_recipes` | `Vec<String>` | Recipes to enable | `[]` |\n");
    md.push_str("| `excluded_paths` | `Vec<String>` | Paths to skip during analysis | `[]` |\n");
    md.push_str("| `max_file_size_kb` | `usize` | Max file size to process | `500` |\n");
    md.push_str("| `dry_run_default` | `bool` | Default to dry-run mode | `true` |\n");
    md.push_str("| `backup_enabled` | `bool` | Automatically create backups | `false` |\n");
    md.push_str("| `preview_lines` | `usize` | Lines to show in diff previews | `100` |\n");
    md.push_str("| `max_files` | `usize` | Max files to process per session | `10000` |\n");
    md.push_str("| `max_duration_seconds` | `u64` | Max execution time | `300` |\n");

    fs::write(dir.join("config.md"), md)?;
    Ok(())
}