diff --git a/crates/oxc_linter/src/rules/unicorn/filename_case.rs b/crates/oxc_linter/src/rules/unicorn/filename_case.rs index 43f029f29886d..6028f32ca7044 100644 --- a/crates/oxc_linter/src/rules/unicorn/filename_case.rs +++ b/crates/oxc_linter/src/rules/unicorn/filename_case.rs @@ -20,10 +20,27 @@ fn join_strings_disjunction(strings: &[String]) -> String { list } -fn filename_case_diagnostic(valid_cases: &[&str]) -> OxcDiagnostic { - let case_names = valid_cases.iter().map(|s| format!("{s} case")).collect::>(); +fn filename_case_diagnostic(filename: &str, valid_cases: &[(&str, Case)]) -> OxcDiagnostic { + let case_names = valid_cases.iter().map(|(name, _)| format!("{name} case")).collect::>(); let message = format!("Filename should be in {}", join_strings_disjunction(&case_names)); - OxcDiagnostic::warn(message).with_label(Span::default()) + + let trimmed_filename = filename.trim_matches('_'); + let converted_filenames = valid_cases + .iter() + .map(|(_, case)| { + let converter = + Converter::new().remove_boundaries(&[Boundary::LowerDigit, Boundary::DigitLower]); + // get the leading characters that were trimmed, if any, else empty string + let leading = filename.chars().take_while(|c| c == &'_').collect::(); + let trailing = filename.chars().rev().take_while(|c| c == &'_').collect::(); + format!("'{leading}{}{trailing}'", converter.to_case(*case).convert(trimmed_filename)) + }) + .collect::>(); + + let help_message = + format!("Rename the file to {}", join_strings_disjunction(&converted_filenames)); + + OxcDiagnostic::warn(message).with_label(Span::default()).with_help(help_message) } #[derive(Debug, Clone)] @@ -122,7 +139,8 @@ impl Rule for FilenameCase { } fn run_once<'a>(&self, ctx: &LintContext<'_>) { - let Some(filename) = ctx.file_path().file_stem().and_then(|s| s.to_str()) else { + let file_path = ctx.file_path(); + let Some(filename) = file_path.file_stem().and_then(|s| s.to_str()) else { return; }; @@ -144,11 +162,14 @@ impl Rule for FilenameCase { Converter::new().remove_boundaries(&[Boundary::LowerDigit, Boundary::DigitLower]); converter.to_case(*case).convert(filename) == filename }) { - let case_names = cases + let valid_cases = cases .iter() - .filter_map(|(enabled, _, name)| if *enabled { Some(*name) } else { None }) + .filter_map( + |(enabled, case, name)| if *enabled { Some((*name, *case)) } else { None }, + ) .collect::>(); - ctx.diagnostic(filename_case_diagnostic(&case_names)); + let filename = file_path.file_name().unwrap().to_string_lossy(); + ctx.diagnostic(filename_case_diagnostic(&filename, &valid_cases)); } } } diff --git a/crates/oxc_linter/src/snapshots/filename_case.snap b/crates/oxc_linter/src/snapshots/filename_case.snap index cecc4d26da91c..283b383a9096b 100644 --- a/crates/oxc_linter/src/snapshots/filename_case.snap +++ b/crates/oxc_linter/src/snapshots/filename_case.snap @@ -4,111 +4,139 @@ source: crates/oxc_linter/src/tester.rs ⚠ eslint-plugin-unicorn(filename-case): Filename should be in kebab case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to 'foo-bar.js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in camel case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to 'fooBar.test.js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in camel case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to 'fooBar.testUtils.js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in snake case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to 'foo_bar.js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in snake case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to 'foo_bar.test.js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in snake case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to 'foo_bar.test_utils.js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in kebab case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to 'foo-bar.js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in kebab case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to 'foo-bar.test.js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in kebab case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to 'foo-bar.test-utils.js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in pascal case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to 'FooBar.js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in pascal case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to 'FooBar.test.js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in pascal case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to 'FooBar.testUtils.js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in camel case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to '_fooBar.js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in camel case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to '___fooBar.js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in snake case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to '_foo_bar.js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in snake case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to '___foo_bar.js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in kebab case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to '_foo-bar.js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in kebab case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to '___foo-bar.js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in pascal case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to '_FooBar.js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in pascal case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to '___FooBar.js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in kebab case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to 'foo-bar.js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in camel case, or pascal case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to 'fooBar.js', or 'FooBar.js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in camel case, kebab case, or pascal case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to '_fooBar.js', '_foo-bar.js', or '_FooBar.js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in snake case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to '_foo_bar.js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in kebab case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to '[foo-bar].js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in kebab case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to '$foo-bar.js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in kebab case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to '$foo-bar.js' ⚠ eslint-plugin-unicorn(filename-case): Filename should be in camel case, kebab case, or pascal case ╭─[filename_case.tsx:1:1] ╰──── + help: Rename the file to '{fooBar}.js', '{foo-bar}.js', or '{fooBar}.js'