-
-
Notifications
You must be signed in to change notification settings - Fork 488
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(linter): eslint-plugin-import/no_unresolved
- Loading branch information
Showing
3 changed files
with
175 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
use oxc_diagnostics::{ | ||
miette::{self, Diagnostic}, | ||
thiserror::Error, | ||
}; | ||
use oxc_macros::declare_oxc_lint; | ||
use oxc_span::Span; | ||
|
||
use crate::{context::LintContext, rule::Rule}; | ||
|
||
#[derive(Debug, Error, Diagnostic)] | ||
#[error("eslint-plugin-import(no-unresolved): Ensure imports point to a file/module that can be resolved")] | ||
#[diagnostic(severity(warning))] | ||
struct NoUnresolvedDiagnostic(#[label] pub Span); | ||
|
||
/// <https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-unresolved.md> | ||
#[derive(Debug, Default, Clone)] | ||
pub struct NoUnresolved; | ||
|
||
declare_oxc_lint!( | ||
/// ### What it does | ||
/// | ||
/// Ensures an imported module can be resolved to a module on the local filesystem. | ||
NoUnresolved, | ||
nursery | ||
); | ||
|
||
impl Rule for NoUnresolved { | ||
fn run_once(&self, ctx: &LintContext<'_>) { | ||
let module_record = ctx.semantic().module_record(); | ||
|
||
for (specifier, spans) in &module_record.requested_modules { | ||
if !module_record.loaded_modules.contains_key(specifier) { | ||
for span in spans { | ||
ctx.diagnostic(NoUnresolvedDiagnostic(*span)); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
#[test] | ||
fn test() { | ||
use crate::tester::Tester; | ||
|
||
let pass = vec![ | ||
// TODO: handle malformed file? | ||
// r#"import "./malformed.js""#, | ||
r#"import foo from "./bar";"#, | ||
r#"import bar from './bar.js';"#, | ||
r#"import {someThing} from './test-module';"#, | ||
// TODO: exclude nodejs builtin modules | ||
// r#"import fs from 'fs';"#, | ||
r#"import('fs');"#, | ||
r#"import('fs');"#, | ||
r#"import * as foo from "a""#, | ||
r#"export { foo } from "./bar""#, | ||
r#"export * from "./bar""#, | ||
r#"let foo; export { foo }"#, | ||
r#"export * as bar from "./bar""#, | ||
// parser: parsers.BABEL_OLD | ||
// r#"export bar from "./bar""#, | ||
r#"import foo from "./jsx/MyUnCoolComponent.jsx""#, | ||
r#"var foo = require("./bar")"#, | ||
r#"require("./bar")"#, | ||
r#"require("./does-not-exist")"#, | ||
r#"require("./does-not-exist")"#, | ||
r#"require(["./bar"], function (bar) {})"#, | ||
r#"define(["./bar"], function (bar) {})"#, | ||
r#"require(["./does-not-exist"], function (bar) {})"#, | ||
r#"define(["require", "exports", "module"], function (r, e, m) { })"#, | ||
r#"require(["./does-not-exist"])"#, | ||
r#"define(["./does-not-exist"], function (bar) {})"#, | ||
r#"require("./does-not-exist", "another arg")"#, | ||
r#"proxyquire("./does-not-exist")"#, | ||
r#"(function() {})("./does-not-exist")"#, | ||
r#"define([0, foo], function (bar) {})"#, | ||
r#"require(0)"#, | ||
r#"require(foo)"#, | ||
]; | ||
|
||
let fail = vec![ | ||
r#"import reallyfake from "./reallyfake/module""#, | ||
r#"import bar from './baz';"#, | ||
r#"import bar from './baz';"#, | ||
r#"import bar from './empty-folder';"#, | ||
r#"import { DEEP } from 'in-alternate-root';"#, | ||
// TODO: dynamic import | ||
// r#"import('in-alternate-root').then(function({DEEP}) {});"#, | ||
r#"export { foo } from "./does-not-exist""#, | ||
r#"export * from "./does-not-exist""#, | ||
// TODO: dynamic import | ||
// r#"import('in-alternate-root').then(function({DEEP}) {});"#, | ||
r#"export * as bar from "./does-not-exist""#, | ||
r#"export bar from "./does-not-exist""#, | ||
r#"var bar = require("./baz")"#, | ||
// TODO: require expression | ||
// r#"require("./baz")"#, | ||
// TODO: amd | ||
// r#"require(["./baz"], function (bar) {})"#, | ||
// r#"define(["./baz"], function (bar) {})"#, | ||
// r#"define(["./baz", "./bar", "./does-not-exist"], function (bar) {})"#, | ||
]; | ||
|
||
Tester::new(NoUnresolved::NAME, pass, fail) | ||
.change_rule_path("index.js") | ||
.with_import_plugin(true) | ||
.test_and_snapshot(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
--- | ||
source: crates/oxc_linter/src/tester.rs | ||
expression: no_unresolved | ||
--- | ||
|
||
⚠ eslint-plugin-import(no-unresolved): Ensure imports point to a file/module that can be resolved | ||
╭─[index.js:1:24] | ||
1 │ import reallyfake from "./reallyfake/module" | ||
· ───────────────────── | ||
╰──── | ||
|
||
⚠ eslint-plugin-import(no-unresolved): Ensure imports point to a file/module that can be resolved | ||
╭─[index.js:1:17] | ||
1 │ import bar from './baz'; | ||
· ─────── | ||
╰──── | ||
|
||
⚠ eslint-plugin-import(no-unresolved): Ensure imports point to a file/module that can be resolved | ||
╭─[index.js:1:17] | ||
1 │ import bar from './baz'; | ||
· ─────── | ||
╰──── | ||
|
||
⚠ eslint-plugin-import(no-unresolved): Ensure imports point to a file/module that can be resolved | ||
╭─[index.js:1:17] | ||
1 │ import bar from './empty-folder'; | ||
· ──────────────── | ||
╰──── | ||
|
||
⚠ eslint-plugin-import(no-unresolved): Ensure imports point to a file/module that can be resolved | ||
╭─[index.js:1:22] | ||
1 │ import { DEEP } from 'in-alternate-root'; | ||
· ─────────────────── | ||
╰──── | ||
|
||
⚠ eslint-plugin-import(no-unresolved): Ensure imports point to a file/module that can be resolved | ||
╭─[index.js:1:21] | ||
1 │ export { foo } from "./does-not-exist" | ||
· ────────────────── | ||
╰──── | ||
|
||
⚠ eslint-plugin-import(no-unresolved): Ensure imports point to a file/module that can be resolved | ||
╭─[index.js:1:15] | ||
1 │ export * from "./does-not-exist" | ||
· ────────────────── | ||
╰──── | ||
|
||
⚠ eslint-plugin-import(no-unresolved): Ensure imports point to a file/module that can be resolved | ||
╭─[index.js:1:22] | ||
1 │ export * as bar from "./does-not-exist" | ||
· ────────────────── | ||
╰──── | ||
|
||
× Unexpected token | ||
╭─[index.js:1:8] | ||
1 │ export bar from "./does-not-exist" | ||
· ─── | ||
╰──── | ||
|
||
⚠ eslint-plugin-import(no-unresolved): Ensure imports point to a file/module that can be resolved | ||
╭─[index.js:1:19] | ||
1 │ var bar = require("./baz") | ||
· ─────── | ||
╰──── | ||
|