feat(linter) eslint-plugin-next no-img-element (#1951)

This commit is contained in:
Cameron 2024-01-09 03:12:01 +00:00 committed by GitHub
parent ac3b44bd0c
commit bce9060095
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 172 additions and 0 deletions

View file

@ -276,6 +276,7 @@ mod nextjs {
pub mod no_assign_module_variable;
pub mod no_async_client_component;
pub mod no_css_tags;
pub mod no_img_element;
}
oxc_macros::declare_all_lint_rules! {
@ -521,4 +522,5 @@ oxc_macros::declare_all_lint_rules! {
nextjs::no_assign_module_variable,
nextjs::no_async_client_component,
nextjs::no_css_tags,
nextjs::no_img_element,
}

View file

@ -0,0 +1,147 @@
use oxc_ast::{ast::JSXElementName, AstKind};
use oxc_diagnostics::{
miette::{self, Diagnostic},
thiserror::Error,
};
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule, AstNode};
#[derive(Debug, Error, Diagnostic)]
#[error("eslint-plugin-next(no-img-element): Prevent usage of `<img>` element due to slower LCP and higher bandwidth.")]
#[diagnostic(severity(warning), help("See https://nextjs.org/docs/messages/no-img-element"))]
struct NoImgElementDiagnostic(#[label] pub Span);
#[derive(Debug, Default, Clone)]
pub struct NoImgElement;
declare_oxc_lint!(
/// ### What it does
///
///
/// ### Why is this bad?
///
///
/// ### Example
/// ```javascript
/// ```
NoImgElement,
correctness
);
impl Rule for NoImgElement {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
let AstKind::JSXOpeningElement(jsx_opening_element) = node.kind() else { return };
let JSXElementName::Identifier(jsx_opening_element_name) = &jsx_opening_element.name else {
return;
};
if jsx_opening_element_name.name.as_str() != "img" {
return;
}
let Some(parent) = ctx.nodes().parent_node(node.id()) else { return };
let Some(parent) = ctx.nodes().parent_node(parent.id()) else { return };
if let AstKind::JSXElement(maybe_picture_jsx_elem) = parent.kind() {
if let JSXElementName::Identifier(jsx_opening_element_name) =
&maybe_picture_jsx_elem.opening_element.name
{
if jsx_opening_element_name.name.as_str() == "picture" {
return;
}
}
}
ctx.diagnostic(NoImgElementDiagnostic(jsx_opening_element_name.span));
}
}
#[test]
fn test() {
use crate::tester::Tester;
let pass = vec![
r#"import { Image } from 'next/image';
export class MyComponent {
render() {
return (
<div>
<Image
src="/test.png"
alt="Test picture"
width={500}
height={500}
/>
</div>
);
}
}"#,
r#"export class MyComponent {
render() {
return (
<picture>
<img
src="/test.png"
alt="Test picture"
width={500}
height={500}
/>
</picture>
);
}
}"#,
r#"export class MyComponent {
render() {
return (
<div>
<picture>
<source media="(min-width:650px)" srcset="/test.jpg"/>
<img
src="/test.png"
alt="Test picture"
style="width:auto;"
/>
</picture>
</div>
);
}
}"#,
];
let fail = vec![
r#"
export class MyComponent {
render() {
return (
<div>
<img
src="/test.png"
alt="Test picture"
width={500}
height={500}
/>
</div>
);
}
}"#,
r#"
export class MyComponent {
render() {
return (
<img
src="/test.png"
alt="Test picture"
width={500}
height={500}
/>
);
}
}"#,
];
Tester::new_without_config(NoImgElement::NAME, pass, fail).test_and_snapshot();
}

View file

@ -0,0 +1,23 @@
---
source: crates/oxc_linter/src/tester.rs
expression: no_img_element
---
⚠ eslint-plugin-next(no-img-element): Prevent usage of `<img>` element due to slower LCP and higher bandwidth.
╭─[no_img_element.tsx:5:1]
5 │ <div>
6 │ <img
· ───
7 │ src="/test.png"
╰────
help: See https://nextjs.org/docs/messages/no-img-element
⚠ eslint-plugin-next(no-img-element): Prevent usage of `<img>` element due to slower LCP and higher bandwidth.
╭─[no_img_element.tsx:4:1]
4 │ return (
5 │ <img
· ───
6 │ src="/test.png"
╰────
help: See https://nextjs.org/docs/messages/no-img-element