feat(linter): add fixer for prefer-node-protocol (#4129)

This commit is contained in:
DonIsaac 2024-07-09 03:13:24 +00:00
parent 2334515247
commit c5b4be021e
2 changed files with 50 additions and 3 deletions

View file

@ -1,5 +1,5 @@
use oxc_ast::{
ast::{Expression, ModuleDeclaration},
ast::{Expression, ModuleDeclaration, TSModuleReference},
AstKind,
};
use oxc_diagnostics::OxcDiagnostic;
@ -43,6 +43,12 @@ impl Rule for PreferNodeProtocol {
}
_ => None,
},
AstKind::TSImportEqualsDeclaration(import) => match &import.module_reference {
TSModuleReference::ExternalModuleReference(external) => {
Some((external.expression.value.clone(), external.expression.span))
}
_ => None,
},
AstKind::CallExpression(call) if !call.optional => {
call.common_js_require().map(|s| (s.value.clone(), s.span))
}
@ -73,7 +79,19 @@ impl Rule for PreferNodeProtocol {
return;
}
ctx.diagnostic(prefer_node_protocol_diagnostic(span, &string_lit_value));
ctx.diagnostic_with_fix(
prefer_node_protocol_diagnostic(span, &string_lit_value),
|fixer| {
// Smallest module name is 2 chars, plus 2 for quotes = 4.
debug_assert!(
span.size() >= 4,
"node stdlib module name should be at least 4 chars long"
);
// We're replacing inside the string literal, shift to account for quotes.
let span = span.shrink_left(1).shrink_right(1);
fixer.replace(span, format!("node:{string_lit_value}"))
},
);
}
}
@ -86,7 +104,10 @@ fn test() {
r#"import fs from "./fs";"#,
r#"import fs from "unknown-builtin-module";"#,
r#"import fs from "node:fs";"#,
r#"import * as fs from "node:fs";"#,
r#"import "punycode / ";"#,
r#"import fs = require("node:fs");"#,
r#"import type fs = require("node:fs");"#,
r#"const fs = require("node:fs");"#,
r#"const fs = require("node:fs/promises");"#,
r"const fs = require(fs);",
@ -103,6 +124,8 @@ fn test() {
let fail = vec![
r#"import fs from "fs";"#,
r#"import * as fs from "fs";"#,
r#"import fs = require("fs");"#,
r#"export {promises} from "fs";"#,
r#"import fs from "fs/promises";"#,
r#"export {default} from "fs/promises";"#,
@ -118,5 +141,15 @@ fn test() {
r"await import('assert/strict')",
];
Tester::new(PreferNodeProtocol::NAME, pass, fail).test_and_snapshot();
let fix = vec![
(r#"import fs from "fs";"#, r#"import fs from "node:fs";"#, None),
(r#"import * as fs from "fs";"#, r#"import * as fs from "node:fs";"#, None),
(r"import fs from 'fs';", r"import fs from 'node:fs';", None),
(r"const fs = require('fs');", r"const fs = require('node:fs');", None),
(r"import fs = require('fs');", r"import fs = require('node:fs');", None),
(r#"import "child_process";"#, r#"import "node:child_process";"#, None),
(r#"import fs from "fs/promises";"#, r#"import fs from "node:fs/promises";"#, None),
];
Tester::new(PreferNodeProtocol::NAME, pass, fail).expect_fix(fix).test_and_snapshot();
}

View file

@ -8,6 +8,20 @@ source: crates/oxc_linter/src/tester.rs
╰────
help: Prefer `node:fs` over `fs`.
⚠ eslint-plugin-unicorn(prefer-node-protocol): Prefer using the `node:` protocol when importing Node.js builtin modules.
╭─[prefer_node_protocol.tsx:1:21]
1 │ import * as fs from "fs";
· ────
╰────
help: Prefer `node:fs` over `fs`.
⚠ eslint-plugin-unicorn(prefer-node-protocol): Prefer using the `node:` protocol when importing Node.js builtin modules.
╭─[prefer_node_protocol.tsx:1:21]
1 │ import fs = require("fs");
· ────
╰────
help: Prefer `node:fs` over `fs`.
⚠ eslint-plugin-unicorn(prefer-node-protocol): Prefer using the `node:` protocol when importing Node.js builtin modules.
╭─[prefer_node_protocol.tsx:1:24]
1 │ export {promises} from "fs";