Skip to content

Commit

Permalink
feat(linter) eslint-plugin-next no-img-element (#1951)
Browse files Browse the repository at this point in the history
  • Loading branch information
camc314 committed Jan 9, 2024
1 parent ac3b44b commit bce9060
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 0 deletions.
2 changes: 2 additions & 0 deletions crates/oxc_linter/src/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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! {
Expand Down Expand Up @@ -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,
}
147 changes: 147 additions & 0 deletions crates/oxc_linter/src/rules/nextjs/no_img_element.rs
Original file line number Diff line number Diff line change
@@ -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();
}
23 changes: 23 additions & 0 deletions crates/oxc_linter/src/snapshots/no_img_element.snap
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit bce9060

Please sign in to comment.