Skip to content

Commit 650ea68

Browse files
committed
refactor(linter): improve nextjs/no-typos rule (#14476)
Improved parts of the code logic.
1 parent d776a17 commit 650ea68

File tree

2 files changed

+41
-58
lines changed

2 files changed

+41
-58
lines changed

crates/oxc_linter/src/rules/nextjs/no_typos.rs

Lines changed: 31 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ use crate::{
1313
};
1414

1515
fn no_typos_diagnostic(typo: &str, suggestion: &str, span: Span) -> OxcDiagnostic {
16-
OxcDiagnostic::warn(format!("{typo} may be a typo. Did you mean {suggestion}?"))
17-
.with_help("Prevent common typos in Next.js's data fetching functions")
16+
OxcDiagnostic::warn(format!("`{typo}` may be a typo. Did you mean `{suggestion}`?"))
17+
.with_help(format!("Change `{typo}` to `{suggestion}`"))
1818
.with_label(span)
1919
}
2020

@@ -57,7 +57,7 @@ const NEXTJS_DATA_FETCHING_FUNCTIONS: [&str; 3] =
5757
["getStaticProps", "getStaticPaths", "getServerSideProps"];
5858

5959
// 0 is the exact match
60-
const THRESHOLD: i32 = 1;
60+
const THRESHOLD: usize = 1;
6161

6262
impl Rule for NoTypos {
6363
fn should_run(&self, ctx: &ContextHost) -> bool {
@@ -80,73 +80,56 @@ impl Rule for NoTypos {
8080
match decl {
8181
Declaration::VariableDeclaration(decl) => {
8282
for decl in &decl.declarations {
83-
let BindingPatternKind::BindingIdentifier(id) = &decl.id.kind else {
84-
continue;
85-
};
86-
let Some(potential_typo) = get_potential_typo(&id.name) else {
87-
continue;
88-
};
89-
ctx.diagnostic(no_typos_diagnostic(
90-
id.name.as_str(),
91-
potential_typo,
92-
id.span,
93-
));
83+
if let BindingPatternKind::BindingIdentifier(id) = &decl.id.kind {
84+
check_function_name(&id.name, id.span, ctx);
85+
}
9486
}
9587
}
9688
Declaration::FunctionDeclaration(decl) => {
97-
let Some(id) = &decl.id else { return };
98-
let Some(potential_typo) = get_potential_typo(&id.name) else {
99-
return;
100-
};
101-
ctx.diagnostic(no_typos_diagnostic(id.name.as_str(), potential_typo, id.span));
89+
if let Some(id) = &decl.id {
90+
check_function_name(&id.name, id.span, ctx);
91+
}
10292
}
10393
_ => {}
10494
}
10595
}
10696
}
10797
}
10898

109-
fn get_potential_typo(fn_name: &str) -> Option<&str> {
110-
let mut potential_typos: Vec<_> = NEXTJS_DATA_FETCHING_FUNCTIONS
111-
.iter()
112-
.map(|&o| {
113-
let distance = min_distance(o, fn_name);
114-
(o, distance)
99+
fn check_function_name(name: &str, span: Span, ctx: &LintContext) {
100+
let mut potential_typos = NEXTJS_DATA_FETCHING_FUNCTIONS
101+
.into_iter()
102+
.filter_map(|o| {
103+
let distance = min_distance(o, name);
104+
(distance <= THRESHOLD && distance > 0).then_some((o, distance))
115105
})
116-
.filter(|&(_, distance)| distance <= THRESHOLD as usize && distance > 0)
117-
.collect();
106+
.collect::<Vec<_>>();
118107

119108
potential_typos.sort_by(|a, b| a.1.cmp(&b.1));
120-
121-
potential_typos.first().map(|(option, _)| *option)
109+
if let Some(suggestion) = potential_typos.first().map(|(option, _)| option) {
110+
ctx.diagnostic(no_typos_diagnostic(name, suggestion, span));
111+
}
122112
}
123113

124-
// the minimum number of operations required to convert string a to string b.
125114
fn min_distance(a: &str, b: &str) -> usize {
126-
let m = a.len();
127-
let n = b.len();
128-
129-
if m < n {
115+
if a.len() < b.len() {
130116
return min_distance(b, a);
131117
}
132118

133-
if n == 0 {
134-
return m;
135-
}
136-
137-
let mut previous_row: Vec<usize> = (0..=n).collect();
119+
let b_chars: Vec<char> = b.chars().collect();
138120

139-
for (i, s1) in a.char_indices() {
140-
let mut current_row = vec![i + 1];
141-
for (j, s2) in b.char_indices() {
142-
let insertions = previous_row[j + 1] + 1;
143-
let deletions = current_row[j] + 1;
144-
let substitutions = previous_row[j] + usize::from(s1 != s2);
145-
current_row.push(insertions.min(deletions).min(substitutions));
121+
let n = b_chars.len();
122+
let mut prev: Vec<usize> = (0..=n).collect();
123+
let mut curr: Vec<usize> = Vec::with_capacity(n + 1);
124+
for (i, ca) in a.chars().enumerate() {
125+
curr.clear();
126+
curr.push(i + 1);
127+
for (j, &cb) in b_chars.iter().enumerate() {
128+
curr.push((prev[j] + usize::from(ca != cb)).min(prev[j + 1] + 1).min(curr[j] + 1));
146129
}
147-
previous_row = current_row;
130+
std::mem::swap(&mut prev, &mut curr);
148131
}
149-
previous_row[n]
132+
prev[n]
150133
}
151134

152135
#[test]
Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,47 @@
11
---
22
source: crates/oxc_linter/src/tester.rs
33
---
4-
eslint-plugin-next(no-typos): getStaticpaths may be a typo. Did you mean getStaticPaths?
4+
eslint-plugin-next(no-typos): `getStaticpaths` may be a typo. Did you mean `getStaticPaths`?
55
╭─[no_typos.tsx:5:30]
66
4 │ }
77
5export const getStaticpaths = async () => {};
88
· ──────────────
99
6export const getStaticProps = async () => {};
1010
╰────
11-
help: Prevent common typos in Next.js's data fetching functions
11+
help: Change `getStaticpaths` to `getStaticPaths`
1212

13-
eslint-plugin-next(no-typos): getStaticPathss may be a typo. Did you mean getStaticPaths?
13+
eslint-plugin-next(no-typos): `getStaticPathss` may be a typo. Did you mean `getStaticPaths`?
1414
╭─[no_typos.tsx:5:39]
1515
4 │ }
1616
5export async function getStaticPathss(){};
1717
· ───────────────
1818
6export async function getStaticPropss(){};
1919
╰────
20-
help: Prevent common typos in Next.js's data fetching functions
20+
help: Change `getStaticPathss` to `getStaticPaths`
2121

22-
eslint-plugin-next(no-typos): getStaticPropss may be a typo. Did you mean getStaticProps?
22+
eslint-plugin-next(no-typos): `getStaticPropss` may be a typo. Did you mean `getStaticProps`?
2323
╭─[no_typos.tsx:6:39]
2424
5export async function getStaticPathss(){};
2525
6export async function getStaticPropss(){};
2626
· ───────────────
2727
7
2828
╰────
29-
help: Prevent common typos in Next.js's data fetching functions
29+
help: Change `getStaticPropss` to `getStaticProps`
3030

31-
eslint-plugin-next(no-typos): getServurSideProps may be a typo. Did you mean getServerSideProps?
31+
eslint-plugin-next(no-typos): `getServurSideProps` may be a typo. Did you mean `getServerSideProps`?
3232
╭─[no_typos.tsx:5:39]
3333
4 │ }
3434
5export async function getServurSideProps(){};
3535
· ──────────────────
3636
6
3737
╰────
38-
help: Prevent common typos in Next.js's data fetching functions
38+
help: Change `getServurSideProps` to `getServerSideProps`
3939

40-
eslint-plugin-next(no-typos): getServurSideProps may be a typo. Did you mean getServerSideProps?
40+
eslint-plugin-next(no-typos): `getServurSideProps` may be a typo. Did you mean `getServerSideProps`?
4141
╭─[no_typos.tsx:5:30]
4242
4 │ }
4343
5export const getServurSideProps = () => {};
4444
· ──────────────────
4545
6
4646
╰────
47-
help: Prevent common typos in Next.js's data fetching functions
47+
help: Change `getServurSideProps` to `getServerSideProps`

0 commit comments

Comments
 (0)