dumpling 0.1.0

A fast JavaScript runtime and bundler in Rust
Documentation
use clap::{Parser, Subcommand};
use std::error::Error;
use std::path::PathBuf;
use tracing::{error, info};

use dumpling::error::Result;

#[derive(Parser)]
#[command(name = "dumpling")]
#[command(about = "A fast JavaScript runtime and bundler")]
pub struct Cli {
    #[command(subcommand)]
    pub command: Commands,
}

#[derive(Subcommand)]
pub enum Commands {
    /// Run a JavaScript file
    Run {
        /// The JavaScript file to execute
        file: PathBuf,
        /// Arguments to pass to the script
        #[arg(trailing_var_arg = true)]
        args: Vec<String>,
    },
/// Bundle JavaScript files
    Bundle {
        /// Entry point file
        entry: PathBuf,
        /// Output file
        #[arg(short, long)]
        output: PathBuf,
        /// Bundle format (iife, esm, cjs)
        #[arg(short, long, default_value = "esm")]
        format: String,
        /// Minify output
        #[arg(short, long)]
        minify: bool,
        /// Generate source map
        #[arg(long)]
        sourcemap: bool,
        /// Generate TypeScript declaration files
        #[arg(long)]
        declarations: bool,
        /// Enable code splitting
        #[arg(long)]
        split: bool,
        /// Vendor chunk size limit (KB)
        #[arg(long, default_value = "50")]
        vendor_size: usize,
    },
    /// Install packages
    Install {
        /// Package names to install
        #[arg(trailing_var_arg = true)]
        packages: Vec<String>,
        /// Save as dev dependency
        #[arg(short, long)]
        dev: bool,
    },
    /// Uninstall packages
    Uninstall {
        /// Package names to remove
        #[arg(trailing_var_arg = true)]
        packages: Vec<String>,
    },
    /// Remove packages not in package.json
    Prune,
    /// Initialize a new project
    Init {
        /// Project name
        #[arg(short, long)]
        name: Option<String>,
    },
    /// Start a development server
    Dev {
        /// Port to serve on
        #[arg(short, long, default_value = "3000")]
        port: u16,
        /// Host to bind to
        #[arg(short, long, default_value = "localhost")]
        host: String,
    },
    /// Run a script from package.json
    RunScript {
        /// Script name to run (e.g., build, test)
        script: String,
    },
    /// Scan for security vulnerabilities
    Audit {
        /// Output format (json, markdown)
        #[arg(short, long, default_value = "markdown")]
        format: String,
        /// Check license compliance
        #[arg(long)]
        licenses: bool,
    },
}

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt::init();

    let cli = Cli::parse();

    if let Err(e) = run(cli).await {
        error!("Error: {}", e);
        // Show error source chain for debugging
        let mut source: Option<&(dyn Error + 'static)> = e.source();
        while let Some(s) = source {
            error!("  Caused by: {}", s);
            source = s.source();
        }
        std::process::exit(1);
    }
}

async fn run(cli: Cli) -> Result<()> {
    match cli.command {
        Commands::Run { file, args } => {
            info!("Running file: {:?}", file);
            dumpling::run_file(file, args).await?;
        }
        Commands::Bundle { entry, output, format, minify, sourcemap, declarations, split, vendor_size } => {
            info!("Bundling {:?} -> {:?}", entry, output);
            dumpling::bundle_with_splitting(entry, output, &format, minify, sourcemap, declarations, split, vendor_size).await?;
        }
        Commands::Install { packages, dev } => {
            info!("Installing packages: {:?}", packages);
            dumpling::install(packages, dev).await?;
        }
        Commands::Uninstall { packages } => {
            info!("Uninstalling packages: {:?}", packages);
            dumpling::uninstall(packages).await?;
        }
        Commands::Prune => {
            info!("Pruning unused packages");
            dumpling::prune().await?;
        }
        Commands::Init { name } => {
            info!("Initializing project");
            dumpling::init(name).await?;
        }
        Commands::Dev { port, host } => {
            info!("Starting dev server on {}:{}", host, port);
            dumpling::start_dev_server(host, port).await?;
        }
        Commands::RunScript { script } => {
            info!("Running script: {}", script);
            dumpling::run_script(script).await?;
        }
        Commands::Audit { format, licenses } => {
            info!("Running security audit");
            dumpling::security::audit(format, licenses).await?;
        }
    }

    Ok(())
}