Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions apps/oxfmt/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ pub struct FormatCommand {
#[bpaf(external, fallback(OutputOptions::DefaultWrite))]
pub output_options: OutputOptions,

#[bpaf(external)]
pub basic_options: BasicOptions,

#[bpaf(external)]
pub misc_options: MiscOptions,

Expand All @@ -49,6 +52,14 @@ pub enum OutputOptions {
ListDifferent,
}

/// Basic Options
#[derive(Debug, Clone, Bpaf)]
pub struct BasicOptions {
/// Path to the configuration file
#[bpaf(short, long, argument("PATH"))]
pub config: Option<PathBuf>,
}

/// Miscellaneous
#[derive(Debug, Clone, Bpaf)]
pub struct MiscOptions {
Expand Down
59 changes: 56 additions & 3 deletions apps/oxfmt/src/format.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
use std::{env, io::Write, path::PathBuf, sync::mpsc, time::Instant};
use std::{
env,
io::Write,
path::{Path, PathBuf},
sync::mpsc,
time::Instant,
};

use ignore::overrides::OverrideBuilder;

use oxc_diagnostics::DiagnosticService;
use oxc_formatter::{FormatOptions, Oxfmtrc};

use crate::{
cli::{CliRunResult, FormatCommand},
Expand Down Expand Up @@ -37,7 +44,19 @@ impl FormatRunner {
let start_time = Instant::now();

let cwd = self.cwd;
let FormatCommand { paths, output_options, misc_options } = self.options;
let FormatCommand { paths, output_options, basic_options, misc_options } = self.options;

// Find and load config
let format_options = match load_config(&cwd, basic_options.config.as_ref()) {
Ok(options) => options,
Err(err) => {
print_and_flush_stdout(
stdout,
&format!("Failed to load configuration file.\n{err}\n"),
);
return CliRunResult::InvalidOptionConfig;
}
};

// Default to current working directory if no paths are provided
let paths = if paths.is_empty() { vec![cwd.clone()] } else { paths };
Expand Down Expand Up @@ -82,7 +101,7 @@ impl FormatRunner {
let output_options_clone = output_options.clone();
// Spawn a thread to run formatting service with streaming entries
rayon::spawn(move || {
let format_service = FormatService::new(cwd, output_options_clone);
let format_service = FormatService::new(cwd, output_options_clone, format_options);
format_service.run_streaming(rx_entry, &tx_error, tx_count);
});

Expand Down Expand Up @@ -156,6 +175,40 @@ impl FormatRunner {
}
}

const DEFAULT_OXFMTRC: &str = ".oxfmtrc.json";

/// # Errors
///
/// Returns error if:
/// - Config file is specified but not found or invalid
/// - Config file parsing fails
fn load_config(cwd: &Path, config: Option<&PathBuf>) -> Result<FormatOptions, String> {
// If `--config` is explicitly specified, use that path directly
if let Some(config_path) = config {
let full_path = if config_path.is_absolute() {
PathBuf::from(config_path)
} else {
cwd.join(config_path)
};

// This will error if the file does not exist or is invalid
let oxfmtrc = Oxfmtrc::from_file(&full_path)?;
return oxfmtrc.into_format_options();
}

// If `--config` is not specified, search the nearest config file from cwd upwards
for dir in cwd.ancestors() {
let config_path = dir.join(DEFAULT_OXFMTRC);
if config_path.exists() {
let oxfmtrc = Oxfmtrc::from_file(&config_path)?;
return oxfmtrc.into_format_options();
}
}

// No config file found, use defaults
Ok(FormatOptions::default())
}

fn print_and_flush_stdout(stdout: &mut dyn Write, message: &str) {
use std::io::{Error, ErrorKind};
fn check_for_writer_error(error: Error) -> Result<(), Error> {
Expand Down
9 changes: 4 additions & 5 deletions apps/oxfmt/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ use crate::{command::OutputOptions, walk::WalkEntry};
pub struct FormatService {
cwd: Box<Path>,
output_options: OutputOptions,
format_options: FormatOptions,
}

impl FormatService {
pub fn new<T>(cwd: T, output_options: OutputOptions) -> Self
pub fn new<T>(cwd: T, output_options: OutputOptions, format_options: FormatOptions) -> Self
where
T: Into<Box<Path>>,
{
Self { cwd: cwd.into(), output_options }
Self { cwd: cwd.into(), output_options, format_options }
}

/// Process entries as they are received from the channel
Expand Down Expand Up @@ -72,9 +73,7 @@ impl FormatService {
return;
}

// TODO: Read and apply config
let options = FormatOptions::default();
let code = Formatter::new(&allocator, options).build(&ret.program);
let code = Formatter::new(&allocator, self.format_options.clone()).build(&ret.program);

let elapsed = start_time.elapsed();
let is_changed = source_text != code;
Expand Down
3 changes: 3 additions & 0 deletions apps/oxfmt/tests/fixtures/config_file/.oxfmtrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"semicolons": "always"
}
3 changes: 3 additions & 0 deletions apps/oxfmt/tests/fixtures/config_file/fmt.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"semicolons": "always"
}
4 changes: 4 additions & 0 deletions apps/oxfmt/tests/fixtures/config_file/fmt.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
// Supports JSONC!
"semicolons": "always"
}
3 changes: 3 additions & 0 deletions apps/oxfmt/tests/fixtures/config_file/nested/.oxfmtrc.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"semicolons": "always"
}
1 change: 1 addition & 0 deletions apps/oxfmt/tests/fixtures/config_file/nested/deep/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
const a = 1
26 changes: 26 additions & 0 deletions apps/oxfmt/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,29 @@ fn write_mode() {
let after = "class Foo {}\n";
Tester::test_write("tests/fixtures/temp.js", before, after);
}

#[test]
fn config_file_auto_discovery() {
Tester::new()
.with_cwd(PathBuf::from("tests/fixtures/config_file"))
.test_and_snapshot_multiple(&[&["--check"]]);

Tester::new()
.with_cwd(PathBuf::from("tests/fixtures/config_file/nested"))
.test_and_snapshot_multiple(&[&["--check"]]);

Tester::new()
.with_cwd(PathBuf::from("tests/fixtures/config_file/nested/deep"))
.test_and_snapshot_multiple(&[&["--check"]]);
}

#[test]
fn config_file_explicit() {
Tester::new().with_cwd(PathBuf::from("tests/fixtures/config_file")).test_and_snapshot_multiple(
&[
&["--check", "--config", "./fmt.json"],
&["--check", "--config", "./fmt.jsonc"],
&["--check", "--config", "NOT_EXISTS.json"],
],
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
source: apps/oxfmt/tests/tester.rs
---
##########
arguments: --check --config ./fmt.json
working directory: tests/fixtures/config_file
----------
Checking formatting...
nested/deep/test.js (<variable>ms)

Format issues found in above 1 files. Run without `--check` to fix.
Finished in <variable>ms on 1 files using 1 threads.
----------
CLI result: FormatMismatch
----------

##########
arguments: --check --config ./fmt.jsonc
working directory: tests/fixtures/config_file
----------
Checking formatting...
nested/deep/test.js (<variable>ms)

Format issues found in above 1 files. Run without `--check` to fix.
Finished in <variable>ms on 1 files using 1 threads.
----------
CLI result: FormatMismatch
----------

##########
arguments: --check --config NOT_EXISTS.json
working directory: tests/fixtures/config_file
----------
Failed to load configuration file.
Failed to read config <cwd>/tests/fixtures/config_file/NOT_EXISTS.json: No such file or directory (os error 2)
----------
CLI result: InvalidOptionConfig
----------
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
source: apps/oxfmt/tests/tester.rs
---
##########
arguments: --check
working directory: tests/fixtures/config_file
----------
Checking formatting...
nested/deep/test.js (<variable>ms)

Format issues found in above 1 files. Run without `--check` to fix.
Finished in <variable>ms on 1 files using 1 threads.
----------
CLI result: FormatMismatch
----------
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
source: apps/oxfmt/tests/tester.rs
---
##########
arguments: --check
working directory: tests/fixtures/config_file/nested
----------
Checking formatting...
deep/test.js (<variable>ms)

Format issues found in above 1 files. Run without `--check` to fix.
Finished in <variable>ms on 1 files using 1 threads.
----------
CLI result: FormatMismatch
----------
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
source: apps/oxfmt/tests/tester.rs
---
##########
arguments: --check
working directory: tests/fixtures/config_file/nested/deep
----------
Checking formatting...
test.js (<variable>ms)

Format issues found in above 1 files. Run without `--check` to fix.
Finished in <variable>ms on 1 files using 1 threads.
----------
CLI result: FormatMismatch
----------
3 changes: 3 additions & 0 deletions crates/oxc_formatter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ oxc_span = { workspace = true }
oxc_syntax = { workspace = true }

cow-utils = { workspace = true }
json-strip-comments = { workspace = true }
rustc-hash = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
unicode-width = "0.2"

[dev-dependencies]
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_formatter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
use write::FormatWrite;

pub use crate::options::*;
pub use crate::service::source_type::get_supported_source_type;
pub use crate::service::{oxfmtrc::Oxfmtrc, source_type::get_supported_source_type};
use crate::{
formatter::{FormatContext, Formatted, format_element::document::Document},
generated::ast_nodes::{AstNode, AstNodes},
Expand Down
1 change: 1 addition & 0 deletions crates/oxc_formatter/src/service/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod oxfmtrc;
pub mod source_type;
Loading
Loading