use super::common::{
fail, first_existing, fixing_enabled, hl, ok, repo_root, resolve_tool, restage,
run as run_tool, run_quiet, staged_files, warn, Restaged,
};
use crate::check::Outcome;
use std::path::Path;
pub const EXTS: &[&str] = &[
".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs", ".json", ".jsonc", ".css", ".scss", ".less",
".html", ".vue", ".md", ".mdx", ".yaml", ".yml",
];
fn has_config(root: &str) -> bool {
if first_existing(
root,
&[
".prettierrc",
"prettier.config.js",
"prettier.config.mjs",
"prettier.config.cjs",
".prettierrc.json",
".prettierrc.yml",
".prettierrc.yaml",
".prettierrc.js",
".prettierrc.cjs",
".prettierrc.mjs",
".prettierrc.toml",
],
)
.is_some()
{
return true;
}
std::fs::read_to_string(Path::new(root).join("package.json"))
.map(|p| p.contains("\"prettier\""))
.unwrap_or(false)
}
pub fn run(_args: &[std::ffi::OsString]) -> Outcome {
let files = staged_files(EXTS);
if files.is_empty() {
return Outcome::Passed;
}
let root = repo_root();
let tool = resolve_tool(&root, "prettier");
let local_pinned = tool
.as_ref()
.map(|t| t[0].contains("node_modules/.bin"))
.unwrap_or(false);
if !has_config(&root) && !local_pinned {
return Outcome::Passed;
}
let Some(argv) = tool else {
warn(&format!(
"prettier config found but no prettier binary. Run {}",
hl("npm install")
));
return Outcome::Unavailable;
};
let mut flags: Vec<String> = Vec::new();
if !Path::new(&root).join(".editorconfig").is_file() {
flags.push("--no-editorconfig".into());
}
let mut check = flags.clone();
check.push("--check".into());
check.push("--".into());
check.extend(files.iter().cloned());
if run_quiet(&root, &argv, &check) {
ok("Prettier passed");
return Outcome::Passed;
}
if fixing_enabled() {
let mut write = flags.clone();
write.push("--write".into());
write.push("--".into());
write.extend(files.iter().cloned());
if run_quiet(&root, &argv, &write) {
match restage(&files) {
Restaged::Staged => {
ok("Prettier reformatted and re-staged");
return Outcome::Fixed;
}
Restaged::Failed(stuck) => {
fail(&format!(
"Prettier reformatted these files but {} failed — the index still \
holds the UNFORMATTED content: {}",
hl("git add"),
stuck.join(", ")
));
return Outcome::Failed;
}
Restaged::Nothing => {}
}
}
}
fail(&format!(
"Prettier found unformatted files. Run {} on:",
hl("prettier --write")
));
let mut list = flags;
list.push("--list-different".into());
list.push("--".into());
list.extend(files);
let _ = run_tool(&root, &argv, &list);
Outcome::Failed
}