Skip to content

Commit c07497c

Browse files
baevmcamc314
andauthored
feat(linter/prefer-modern-dom-apis): implement suggestion (#17965)
this PR adds suggestion for `unicorn/prefer-modern-dom-apis` rule. used tests from #17475 issue #684 --------- Co-authored-by: Cameron <cameron.clark@hey.com>
1 parent 5b11610 commit c07497c

File tree

2 files changed

+60
-10
lines changed

2 files changed

+60
-10
lines changed

crates/oxc_linter/src/rules/unicorn/prefer_modern_dom_apis.rs

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use oxc_ast::{
44
};
55
use oxc_diagnostics::OxcDiagnostic;
66
use oxc_macros::declare_oxc_lint;
7-
use oxc_span::Span;
7+
use oxc_span::{GetSpan, Span};
88

99
use crate::{AstNode, ast_util::is_method_call, context::LintContext, rule::Rule};
1010

@@ -38,6 +38,16 @@ fn get_replacement_for_position(position: &str) -> Option<&'static str> {
3838
}
3939
}
4040

41+
fn is_value_not_usable(node: &AstNode, ctx: &LintContext) -> bool {
42+
let parent_node = ctx.nodes().parent_node(node.id());
43+
let grandparent_node = ctx.nodes().parent_node(parent_node.id());
44+
matches!(
45+
(parent_node.kind(), grandparent_node.kind()),
46+
(AstKind::ExpressionStatement(_), _)
47+
| (AstKind::ChainExpression(_), AstKind::ExpressionStatement(_))
48+
)
49+
}
50+
4151
declare_oxc_lint!(
4252
/// ### What it does
4353
///
@@ -68,7 +78,7 @@ declare_oxc_lint!(
6878
PreferModernDomApis,
6979
unicorn,
7080
style,
71-
pending
81+
suggestion
7282
);
7383

7484
impl Rule for PreferModernDomApis {
@@ -96,11 +106,24 @@ impl Rule for PreferModernDomApis {
96106
&& !call_expr.optional
97107
&& let Some(preferred_method) = get_replacement_for_disallowed_method(method)
98108
{
99-
ctx.diagnostic(prefer_modern_dom_apis_diagnostic(
109+
let diagnostic = prefer_modern_dom_apis_diagnostic(
100110
preferred_method,
101111
method,
102112
member_expr.property.span,
103-
));
113+
);
114+
115+
if is_value_not_usable(node, ctx) {
116+
ctx.diagnostic_with_suggestion(diagnostic, |fixer| {
117+
let new_node = ctx.source_range(call_expr.arguments[0].span());
118+
let old_node = ctx.source_range(call_expr.arguments[1].span());
119+
120+
let replacement = format!("{old_node}.{preferred_method}({new_node})");
121+
122+
fixer.replace(call_expr.span, replacement)
123+
});
124+
} else {
125+
ctx.diagnostic(diagnostic);
126+
}
104127

105128
return;
106129
}
@@ -112,13 +135,28 @@ impl Rule for PreferModernDomApis {
112135
Some(2),
113136
Some(2),
114137
) && let Argument::StringLiteral(lit) = &call_expr.arguments[0]
115-
&& let Some(replacer) = get_replacement_for_position(lit.value.as_str())
138+
&& let Some(preferred_method) = get_replacement_for_position(lit.value.as_str())
116139
{
117-
ctx.diagnostic(prefer_modern_dom_apis_diagnostic(
118-
replacer,
140+
let diagnostic = prefer_modern_dom_apis_diagnostic(
141+
preferred_method,
119142
method,
120143
member_expr.property.span,
121-
));
144+
);
145+
146+
let can_fix = method == "insertAdjacentText" || is_value_not_usable(node, ctx);
147+
148+
if can_fix {
149+
ctx.diagnostic_with_suggestion(diagnostic, |fixer| {
150+
let content = ctx.source_range(call_expr.arguments[1].span());
151+
let reference = ctx.source_range(member_expr.object.span());
152+
153+
let replacement = format!("{reference}.{preferred_method}({content})");
154+
155+
fixer.replace(call_expr.span, replacement)
156+
});
157+
} else {
158+
ctx.diagnostic(diagnostic);
159+
}
122160
}
123161
}
124162
}
@@ -207,8 +245,7 @@ fn test() {
207245
),
208246
];
209247

210-
// TODO: Implement autofix and use these tests.
211-
let _fix = vec![
248+
let fix = vec![
212249
(
213250
"parentNode.replaceChild(newChildNode, oldChildNode);",
214251
"oldChildNode.replaceWith(newChildNode);",
@@ -285,5 +322,6 @@ fn test() {
285322
];
286323

287324
Tester::new(PreferModernDomApis::NAME, PreferModernDomApis::PLUGIN, pass, fail)
325+
.expect_fix(fix)
288326
.test_and_snapshot();
289327
}

crates/oxc_linter/src/snapshots/unicorn_prefer_modern_dom_apis.snap

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ source: crates/oxc_linter/src/tester.rs
66
1parentNode.replaceChild(newChildNode, oldChildNode);
77
· ────────────
88
╰────
9+
help: Replace `parentNode.replaceChild(newChildNode, oldChildNode)` with `oldChildNode.replaceWith(newChildNode)`.
910

1011
eslint-plugin-unicorn(prefer-modern-dom-apis): Prefer using `replaceWith` over `replaceChild`.
1112
╭─[prefer_modern_dom_apis.tsx:1:24]
@@ -24,6 +25,7 @@ source: crates/oxc_linter/src/tester.rs
2425
1parentNode.insertBefore(newNode, referenceNode);
2526
· ────────────
2627
╰────
28+
help: Replace `parentNode.insertBefore(newNode, referenceNode)` with `referenceNode.before(newNode)`.
2729

2830
eslint-plugin-unicorn(prefer-modern-dom-apis): Prefer using `before` over `insertBefore`.
2931
╭─[prefer_modern_dom_apis.tsx:1:12]
@@ -60,60 +62,70 @@ source: crates/oxc_linter/src/tester.rs
6062
1referenceNode.insertAdjacentText("beforebegin", "text");
6163
· ──────────────────
6264
╰────
65+
help: Replace `referenceNode.insertAdjacentText("beforebegin", "text")` with `referenceNode.before("text")`.
6366

6467
eslint-plugin-unicorn(prefer-modern-dom-apis): Prefer using `prepend` over `insertAdjacentText`.
6568
╭─[prefer_modern_dom_apis.tsx:1:15]
6669
1referenceNode.insertAdjacentText("afterbegin", "text");
6770
· ──────────────────
6871
╰────
72+
help: Replace `referenceNode.insertAdjacentText("afterbegin", "text")` with `referenceNode.prepend("text")`.
6973

7074
eslint-plugin-unicorn(prefer-modern-dom-apis): Prefer using `append` over `insertAdjacentText`.
7175
╭─[prefer_modern_dom_apis.tsx:1:15]
7276
1referenceNode.insertAdjacentText("beforeend", "text");
7377
· ──────────────────
7478
╰────
79+
help: Replace `referenceNode.insertAdjacentText("beforeend", "text")` with `referenceNode.append("text")`.
7580

7681
eslint-plugin-unicorn(prefer-modern-dom-apis): Prefer using `after` over `insertAdjacentText`.
7782
╭─[prefer_modern_dom_apis.tsx:1:15]
7883
1referenceNode.insertAdjacentText("afterend", "text");
7984
· ──────────────────
8085
╰────
86+
help: Replace `referenceNode.insertAdjacentText("afterend", "text")` with `referenceNode.after("text")`.
8187

8288
eslint-plugin-unicorn(prefer-modern-dom-apis): Prefer using `before` over `insertAdjacentText`.
8389
╭─[prefer_modern_dom_apis.tsx:1:27]
8490
1const foo = referenceNode.insertAdjacentText("beforebegin", "text");
8591
· ──────────────────
8692
╰────
93+
help: Replace `referenceNode.insertAdjacentText("beforebegin", "text")` with `referenceNode.before("text")`.
8794

8895
eslint-plugin-unicorn(prefer-modern-dom-apis): Prefer using `before` over `insertAdjacentText`.
8996
╭─[prefer_modern_dom_apis.tsx:1:21]
9097
1foo = referenceNode.insertAdjacentText("beforebegin", "text");
9198
· ──────────────────
9299
╰────
100+
help: Replace `referenceNode.insertAdjacentText("beforebegin", "text")` with `referenceNode.before("text")`.
93101

94102
eslint-plugin-unicorn(prefer-modern-dom-apis): Prefer using `before` over `insertAdjacentElement`.
95103
╭─[prefer_modern_dom_apis.tsx:1:15]
96104
1referenceNode.insertAdjacentElement("beforebegin", newNode);
97105
· ─────────────────────
98106
╰────
107+
help: Replace `referenceNode.insertAdjacentElement("beforebegin", newNode)` with `referenceNode.before(newNode)`.
99108

100109
eslint-plugin-unicorn(prefer-modern-dom-apis): Prefer using `prepend` over `insertAdjacentElement`.
101110
╭─[prefer_modern_dom_apis.tsx:1:15]
102111
1referenceNode.insertAdjacentElement("afterbegin", "text");
103112
· ─────────────────────
104113
╰────
114+
help: Replace `referenceNode.insertAdjacentElement("afterbegin", "text")` with `referenceNode.prepend("text")`.
105115

106116
eslint-plugin-unicorn(prefer-modern-dom-apis): Prefer using `append` over `insertAdjacentElement`.
107117
╭─[prefer_modern_dom_apis.tsx:1:15]
108118
1referenceNode.insertAdjacentElement("beforeend", "text");
109119
· ─────────────────────
110120
╰────
121+
help: Replace `referenceNode.insertAdjacentElement("beforeend", "text")` with `referenceNode.append("text")`.
111122

112123
eslint-plugin-unicorn(prefer-modern-dom-apis): Prefer using `after` over `insertAdjacentElement`.
113124
╭─[prefer_modern_dom_apis.tsx:1:15]
114125
1referenceNode.insertAdjacentElement("afterend", newNode);
115126
· ─────────────────────
116127
╰────
128+
help: Replace `referenceNode.insertAdjacentElement("afterend", newNode)` with `referenceNode.after(newNode)`.
117129

118130
eslint-plugin-unicorn(prefer-modern-dom-apis): Prefer using `before` over `insertAdjacentElement`.
119131
╭─[prefer_modern_dom_apis.tsx:1:27]

0 commit comments

Comments
 (0)