use color_eyre::eyre::Result;
use nargo_bundler::{BuildOutputs, Bundler, OutputFormat};
use nargo_compiler::Compiler;
use nargo_config::ConfigLoader;
use nargo_ir::IRModule;
use std::{
fs,
path::{Path, PathBuf},
};
use walkdir::WalkDir;
#[derive(Debug, Clone)]
pub struct BuildConfig {
pub root: PathBuf,
pub out_dir: PathBuf,
pub is_production: bool,
pub source_map: bool,
pub incremental: bool,
}
impl BuildConfig {
pub fn new(root: PathBuf, out_dir: PathBuf) -> Self {
Self { root, out_dir, is_production: true, source_map: true, incremental: true }
}
pub fn with_production(mut self, is_production: bool) -> Self {
self.is_production = is_production;
self
}
pub fn with_source_map(mut self, source_map: bool) -> Self {
self.source_map = source_map;
self
}
pub fn with_incremental(mut self, incremental: bool) -> Self {
self.incremental = incremental;
self
}
}
pub struct BuildContext {
pub config: BuildConfig,
pub nargo_config: nargo_config::NargoConfig,
pub compiler: Compiler,
pub bundler: Bundler,
}
impl BuildContext {
pub async fn new(config: BuildConfig) -> Result<Self> {
let config_path = nargo_config::ConfigLoader::find_config_file(&config.root).unwrap_or_else(|| config.root.join(nargo_config::DEFAULT_CONFIG_FILE));
let config_loader = nargo_config::ConfigLoader::new(config_path);
let nargo_config = config_loader.load()?;
let compiler = Compiler::new();
let bundler = Bundler::new(None);
Ok(Self { config, nargo_config, compiler, bundler })
}
pub fn load_and_compile_files(&mut self) -> Result<Vec<IRModule>> {
let source_files = self.find_source_files()?;
let mut modules = Vec::with_capacity(source_files.len());
for file_path in source_files {
let module = self.compile_file(&file_path)?;
modules.push(module);
}
Ok(modules)
}
fn find_source_files(&self) -> Result<Vec<PathBuf>> {
let mut files = Vec::new();
let src_dir = self.config.root.join("src");
if !src_dir.exists() {
return Ok(files);
}
for entry in WalkDir::new(&src_dir).into_iter().filter_map(|e| e.ok()) {
let path = entry.path();
if path.extension().and_then(|ext| ext.to_str()).map(|ext| matches!(ext, "nargo" | "vmz" | "ts" | "tsx")).unwrap_or(false) {
files.push(path.to_path_buf());
}
}
Ok(files)
}
fn compile_file(&mut self, file_path: &Path) -> Result<IRModule> {
let content = fs::read_to_string(file_path)?;
let file_name = file_path.file_stem().and_then(|s| s.to_str()).unwrap_or("unknown");
let mut options = nargo_compiler::CompileOptions::default();
options.is_prod = self.config.is_production;
let ir = self.compiler.compile_to_ir(file_name, &content, &mut options)?;
Ok(ir)
}
pub fn build(&mut self, modules: &[IRModule]) -> Result<BuildOutputs> {
self.bundler.analyze_all(modules);
let outputs = self.bundler.bundle_all(modules, OutputFormat::JavaScript)?;
Ok(outputs)
}
pub fn write_outputs(&self, outputs: &BuildOutputs) -> Result<()> {
fs::create_dir_all(&self.config.out_dir)?;
for output in &outputs.outputs {
let output_path = self.config.out_dir.join(&output.filename);
fs::write(&output_path, &output.code)?;
}
Ok(())
}
}
pub async fn execute_build(root: &PathBuf, out_dir: &PathBuf) -> Result<()> {
println!("🏗️ Building project in {} to {}", root.display(), out_dir.display());
let config = BuildConfig::new(root.clone(), out_dir.clone());
let mut context = BuildContext::new(config).await?;
println!("📦 Loading and compiling source files...");
let modules = context.load_and_compile_files()?;
if modules.is_empty() {
println!("⚠️ No source files found");
return Ok(());
}
println!("🔗 Bundling modules...");
let outputs = context.build(&modules)?;
println!("💾 Writing output files...");
context.write_outputs(&outputs)?;
println!("✅ Build completed! Generated {} files", outputs.outputs.len());
Ok(())
}
pub async fn execute_compile(root: &PathBuf, watch: bool) -> Result<()> {
println!("🔄 Compiling project in {}", root.display());
if watch {
println!("👁️ Watch mode enabled");
}
let out_dir = root.join("dist");
let config = BuildConfig::new(root.clone(), out_dir);
let mut context = BuildContext::new(config).await?;
let modules = context.load_and_compile_files()?;
if modules.is_empty() {
println!("⚠️ No source files found");
return Ok(());
}
println!("✅ Compilation completed! Compiled {} modules", modules.len());
Ok(())
}