mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(linter): eslint-plugin-import/no_unresolved (#2475)
This commit is contained in:
parent
35a0f895dc
commit
f1e364fee5
3 changed files with 175 additions and 0 deletions
|
|
@ -17,6 +17,7 @@ mod import {
|
||||||
pub mod no_named_as_default;
|
pub mod no_named_as_default;
|
||||||
pub mod no_named_as_default_member;
|
pub mod no_named_as_default_member;
|
||||||
pub mod no_self_import;
|
pub mod no_self_import;
|
||||||
|
pub mod no_unresolved;
|
||||||
pub mod no_unused_modules;
|
pub mod no_unused_modules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -557,6 +558,7 @@ oxc_macros::declare_all_lint_rules! {
|
||||||
import::no_named_as_default,
|
import::no_named_as_default,
|
||||||
import::no_named_as_default_member,
|
import::no_named_as_default_member,
|
||||||
import::no_self_import,
|
import::no_self_import,
|
||||||
|
import::no_unresolved,
|
||||||
import::no_unused_modules,
|
import::no_unused_modules,
|
||||||
jsx_a11y::alt_text,
|
jsx_a11y::alt_text,
|
||||||
jsx_a11y::anchor_has_content,
|
jsx_a11y::anchor_has_content,
|
||||||
|
|
|
||||||
108
crates/oxc_linter/src/rules/import/no_unresolved.rs
Normal file
108
crates/oxc_linter/src/rules/import/no_unresolved.rs
Normal file
|
|
@ -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();
|
||||||
|
}
|
||||||
65
crates/oxc_linter/src/snapshots/no_unresolved.snap
Normal file
65
crates/oxc_linter/src/snapshots/no_unresolved.snap
Normal file
|
|
@ -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")
|
||||||
|
· ───────
|
||||||
|
╰────
|
||||||
|
|
||||||
Loading…
Reference in a new issue