diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_string_replace_all.rs b/crates/oxc_linter/src/rules/unicorn/prefer_string_replace_all.rs index 7599249bd2bb9..149091f93cd87 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_string_replace_all.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_string_replace_all.rs @@ -1,11 +1,13 @@ +use oxc_allocator::Allocator; use oxc_ast::{ - AstKind, + AstBuilder, AstKind, ast::{Argument, MemberExpression, RegExpFlags}, }; +use oxc_codegen::CodegenOptions; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_regular_expression::ast::Term; -use oxc_span::{CompactStr, GetSpan, Span}; +use oxc_span::{CompactStr, GetSpan, SPAN, Span}; use crate::{AstNode, ast_util::extract_regex_flags, context::LintContext, rule::Rule}; @@ -81,8 +83,19 @@ impl Rule for PreferStringReplaceAll { "replaceAll" => { if let Some(k) = get_pattern_replacement(pattern) { ctx.diagnostic_with_fix(string_literal(pattern.span(), &k), |fixer| { - // foo.replaceAll(/hello world/g, bar) => foo.replaceAll("hello world", bar) - fixer.replace(pattern.span(), format!("{k:?}")) + // foo.replaceAll(/hello world/g, bar) => foo.replaceAll('hello world', bar) + let mut codegen = fixer.codegen().with_options(CodegenOptions { + single_quote: true, + ..Default::default() + }); + let alloc = Allocator::default(); + let ast = AstBuilder::new(&alloc); + codegen.print_expression(&ast.expression_string_literal( + SPAN, + ast.atom(&k), + None, + )); + fixer.replace(pattern.span(), codegen.into_source_text()) }); } } @@ -137,7 +150,22 @@ fn get_pattern_replacement<'a>(expr: &'a Argument<'a>) -> Option { return None; } - Some(CompactStr::new(reg_exp_literal.regex.pattern.text.as_str())) + // Convert the regex pattern to a string by extracting character values + // from the parsed AST instead of using the raw source text. + // This ensures escape sequences are properly handled. + let mut result = String::new(); + for term in pattern_terms { + if let Term::Character(ch) = term { + if let Some(c) = char::from_u32(ch.value) { + result.push(c); + } else { + // Invalid character, fall back to source text + return Some(CompactStr::new(reg_exp_literal.regex.pattern.text.as_str())); + } + } + } + + Some(CompactStr::new(&result)) } #[test] @@ -230,7 +258,8 @@ fn test() { let fix = vec![ ("foo.replace(/a/g, bar)", "foo.replaceAll(/a/g, bar)"), - ("foo.replaceAll(/a/g, bar)", "foo.replaceAll(\"a\", bar)"), + ("foo.replaceAll(/a/g, bar)", "foo.replaceAll('a', bar)"), + (r"text.replaceAll(/\\`/g, '`')", r"text.replaceAll('\\`', '`')"), ]; Tester::new(PreferStringReplaceAll::NAME, PreferStringReplaceAll::PLUGIN, pass, fail) diff --git a/crates/oxc_linter/src/snapshots/unicorn_prefer_string_replace_all.snap b/crates/oxc_linter/src/snapshots/unicorn_prefer_string_replace_all.snap index a5602f38002f9..24a23202cc56d 100644 --- a/crates/oxc_linter/src/snapshots/unicorn_prefer_string_replace_all.snap +++ b/crates/oxc_linter/src/snapshots/unicorn_prefer_string_replace_all.snap @@ -251,14 +251,14 @@ source: crates/oxc_linter/src/tester.rs 1 │ foo.replaceAll(/a]/g, _) · ───── ╰──── - help: Replace `/a]/g` with `"a]"`. + help: Replace `/a]/g` with `'a]'`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): This pattern can be replaced with `a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long string`. ╭─[prefer_string_replace_all.tsx:1:16] 1 │ foo.replaceAll(/a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long string/g, _) · ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ╰──── - help: Replace `/a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long string/g` with `"a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long string"`. + help: Replace `/a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long string/g` with `'a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long string'`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] @@ -272,4 +272,4 @@ source: crates/oxc_linter/src/tester.rs 1 │ "Hello world".replaceAll(/world/g, 'world!'); · ──────── ╰──── - help: Replace `/world/g` with `"world"`. + help: Replace `/world/g` with `'world'`.