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
Original file line number Diff line number Diff line change
@@ -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};

Expand Down Expand Up @@ -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())
});
}
}
Expand Down Expand Up @@ -137,7 +150,22 @@ fn get_pattern_replacement<'a>(expr: &'a Argument<'a>) -> Option<CompactStr> {
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]
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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'`.
Loading