diff --git a/cli/fmt.rs b/cli/fmt.rs index 9b6e5975dd..c297f042ce 100644 --- a/cli/fmt.rs +++ b/cli/fmt.rs @@ -21,6 +21,8 @@ use std::path::PathBuf; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; +const BOM_CHAR: char = '\u{FEFF}'; + /// Format JavaScript/TypeScript files. /// /// First argument supports globs, and if it is `None` @@ -66,11 +68,11 @@ async fn check_source_files( run_parallelized(paths, { let not_formatted_files_count = not_formatted_files_count.clone(); move |file_path| { - let file_contents = fs::read_to_string(&file_path)?; - let r = formatter.format_text(&file_path, &file_contents); + let file_text = read_file_contents(&file_path)?.text; + let r = formatter.format_text(&file_path, &file_text); match r { Ok(formatted_text) => { - if formatted_text != file_contents { + if formatted_text != file_text { not_formatted_files_count.fetch_add(1, Ordering::SeqCst); } } @@ -112,12 +114,18 @@ async fn format_source_files( run_parallelized(paths, { let formatted_files_count = formatted_files_count.clone(); move |file_path| { - let file_contents = fs::read_to_string(&file_path)?; - let r = formatter.format_text(&file_path, &file_contents); + let file_contents = read_file_contents(&file_path)?; + let r = formatter.format_text(&file_path, &file_contents.text); match r { Ok(formatted_text) => { - if formatted_text != file_contents { - fs::write(&file_path, formatted_text)?; + if formatted_text != file_contents.text { + write_file_contents( + &file_path, + FileContents { + had_bom: file_contents.had_bom, + text: formatted_text, + }, + )?; formatted_files_count.fetch_add(1, Ordering::SeqCst); let _g = output_lock.lock().unwrap(); println!("{}", file_path.to_string_lossy()); @@ -203,6 +211,38 @@ fn get_config() -> dprint::configuration::Configuration { ConfigurationBuilder::new().deno().build() } +struct FileContents { + text: String, + had_bom: bool, +} + +fn read_file_contents(file_path: &PathBuf) -> Result { + let file_text = fs::read_to_string(&file_path)?; + let had_bom = file_text.starts_with(BOM_CHAR); + let text = if had_bom { + // remove the BOM + String::from(&file_text[BOM_CHAR.len_utf8()..]) + } else { + file_text + }; + + Ok(FileContents { text, had_bom }) +} + +fn write_file_contents( + file_path: &PathBuf, + file_contents: FileContents, +) -> Result<(), ErrBox> { + let file_text = if file_contents.had_bom { + // add back the BOM + format!("{}{}", BOM_CHAR, file_contents.text) + } else { + file_contents.text + }; + + Ok(fs::write(file_path, file_text)?) +} + async fn run_parallelized( file_paths: Vec, f: F,