|
1 | | -use std::{env, io::Write, path::PathBuf, sync::mpsc, time::Instant}; |
| 1 | +use std::{ |
| 2 | + env, |
| 3 | + io::Write, |
| 4 | + path::{Path, PathBuf}, |
| 5 | + sync::mpsc, |
| 6 | + time::Instant, |
| 7 | +}; |
2 | 8 |
|
3 | 9 | use ignore::overrides::OverrideBuilder; |
4 | 10 |
|
5 | 11 | use oxc_diagnostics::DiagnosticService; |
| 12 | +use oxc_formatter::{FormatOptions, Oxfmtrc}; |
6 | 13 |
|
7 | 14 | use crate::{ |
8 | 15 | cli::{CliRunResult, FormatCommand}, |
@@ -37,7 +44,19 @@ impl FormatRunner { |
37 | 44 | let start_time = Instant::now(); |
38 | 45 |
|
39 | 46 | let cwd = self.cwd; |
40 | | - let FormatCommand { paths, output_options, misc_options } = self.options; |
| 47 | + let FormatCommand { paths, output_options, basic_options, misc_options } = self.options; |
| 48 | + |
| 49 | + // Find and load config |
| 50 | + let format_options = match load_config(&cwd, basic_options.config.as_ref()) { |
| 51 | + Ok(options) => options, |
| 52 | + Err(err) => { |
| 53 | + print_and_flush_stdout( |
| 54 | + stdout, |
| 55 | + &format!("Failed to load configuration file.\n{err}\n"), |
| 56 | + ); |
| 57 | + return CliRunResult::InvalidOptionConfig; |
| 58 | + } |
| 59 | + }; |
41 | 60 |
|
42 | 61 | // Default to current working directory if no paths are provided |
43 | 62 | let paths = if paths.is_empty() { vec![cwd.clone()] } else { paths }; |
@@ -82,7 +101,7 @@ impl FormatRunner { |
82 | 101 | let output_options_clone = output_options.clone(); |
83 | 102 | // Spawn a thread to run formatting service with streaming entries |
84 | 103 | rayon::spawn(move || { |
85 | | - let format_service = FormatService::new(cwd, output_options_clone); |
| 104 | + let format_service = FormatService::new(cwd, output_options_clone, format_options); |
86 | 105 | format_service.run_streaming(rx_entry, &tx_error, tx_count); |
87 | 106 | }); |
88 | 107 |
|
@@ -156,6 +175,40 @@ impl FormatRunner { |
156 | 175 | } |
157 | 176 | } |
158 | 177 |
|
| 178 | +const DEFAULT_OXFMTRC: &str = ".oxfmtrc.json"; |
| 179 | + |
| 180 | +/// # Errors |
| 181 | +/// |
| 182 | +/// Returns error if: |
| 183 | +/// - Config file is specified but not found or invalid |
| 184 | +/// - Config file parsing fails |
| 185 | +fn load_config(cwd: &Path, config: Option<&PathBuf>) -> Result<FormatOptions, String> { |
| 186 | + // If `--config` is explicitly specified, use that path directly |
| 187 | + if let Some(config_path) = config { |
| 188 | + let full_path = if config_path.is_absolute() { |
| 189 | + PathBuf::from(config_path) |
| 190 | + } else { |
| 191 | + cwd.join(config_path) |
| 192 | + }; |
| 193 | + |
| 194 | + // This will error if the file does not exist or is invalid |
| 195 | + let oxfmtrc = Oxfmtrc::from_file(&full_path)?; |
| 196 | + return oxfmtrc.into_format_options(); |
| 197 | + } |
| 198 | + |
| 199 | + // If `--config` is not specified, search the nearest config file from cwd upwards |
| 200 | + for dir in cwd.ancestors() { |
| 201 | + let config_path = dir.join(DEFAULT_OXFMTRC); |
| 202 | + if config_path.exists() { |
| 203 | + let oxfmtrc = Oxfmtrc::from_file(&config_path)?; |
| 204 | + return oxfmtrc.into_format_options(); |
| 205 | + } |
| 206 | + } |
| 207 | + |
| 208 | + // No config file found, use defaults |
| 209 | + Ok(FormatOptions::default()) |
| 210 | +} |
| 211 | + |
159 | 212 | fn print_and_flush_stdout(stdout: &mut dyn Write, message: &str) { |
160 | 213 | use std::io::{Error, ErrorKind}; |
161 | 214 | fn check_for_writer_error(error: Error) -> Result<(), Error> { |
|
0 commit comments