Skip to content

Commit

Permalink
feat(linter): eslint-plugin-react: no-unknown-property (#1875)
Browse files Browse the repository at this point in the history
  • Loading branch information
XantreDev authored Jan 7, 2024
1 parent f46ed71 commit c6eb519
Show file tree
Hide file tree
Showing 7 changed files with 1,057 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion crates/oxc_language_server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ env_logger = { workspace = true }
futures = { workspace = true }
ignore = { workspace = true, features = ["simd-accel"] }
miette = { workspace = true, features = ["fancy-no-backtrace"] }
rayon = { workspace = true }
ropey = { workspace = true }
tokio = { workspace = true, features = ["full"] }
tower-lsp = { workspace = true, features = ["proposed"] }
Expand Down
1 change: 1 addition & 0 deletions crates/oxc_linter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ oxc_resolver = { version = "1.1.0" }
rayon = { workspace = true }
lazy_static = { workspace = true } # used in oxc_macros
serde_json = { workspace = true }
serde = { workspace = true }
regex = { workspace = true }
rustc-hash = { workspace = true }
phf = { workspace = true, features = ["macros"] }
Expand Down
2 changes: 2 additions & 0 deletions crates/oxc_linter/src/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ mod react {
pub mod no_render_return_value;
pub mod no_string_refs;
pub mod no_unescaped_entities;
pub mod no_unknown_property;
pub mod react_in_jsx_scope;
}

Expand Down Expand Up @@ -469,6 +470,7 @@ oxc_macros::declare_all_lint_rules! {
react::no_string_refs,
react::no_unescaped_entities,
react::no_is_mounted,
react::no_unknown_property,
import::default,
import::named,
import::no_cycle,
Expand Down
734 changes: 734 additions & 0 deletions crates/oxc_linter/src/rules/react/no_unknown_property.rs

Large diffs are not rendered by default.

310 changes: 310 additions & 0 deletions crates/oxc_linter/src/snapshots/no_unknown_property.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,310 @@
---
source: crates/oxc_linter/src/tester.rs
assertion_line: 144
expression: no_unknown_property
---
eslint-plugin-react(no-unknown-property): Unknown property found
╭─[no_unknown_property.tsx:1:1]
1<div allowTransparency="true" />
· ─────────────────
╰────
help: Remove unknown property

eslint-plugin-react(no-unknown-property): Unknown property found
╭─[no_unknown_property.tsx:1:1]
1<div hasOwnProperty="should not be allowed property"></div>;
· ──────────────
╰────
help: Remove unknown property

eslint-plugin-react(no-unknown-property): Unknown property found
╭─[no_unknown_property.tsx:1:1]
1<div abc="should not be allowed property"></div>;
· ───
╰────
help: Remove unknown property

eslint-plugin-react(no-unknown-property): Unknown property found
╭─[no_unknown_property.tsx:1:1]
1<div aria-fake="should not be allowed property"></div>;
· ─────────
╰────
help: Remove unknown property

eslint-plugin-react(no-unknown-property): Unknown property found
╭─[no_unknown_property.tsx:1:1]
1<div someProp="bar"></div>;
· ────────
╰────
help: Remove unknown property

eslint-plugin-react(no-unknown-property): Unknown property found
╭─[no_unknown_property.tsx:1:1]
1<div class="bar"></div>;
· ─────
╰────
help: Use 'className' instead

eslint-plugin-react(no-unknown-property): Unknown property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div for="bar"></div>;
· ───
╰────
help: Use 'htmlFor' instead

eslint-plugin-react(no-unknown-property): Unknown property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div accept-charset="bar"></div>;
· ──────────────
╰────
help: Use 'acceptCharset' instead

eslint-plugin-react(no-unknown-property): Unknown property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div http-equiv="bar"></div>;
· ──────────
╰────
help: Use 'httpEquiv' instead

eslint-plugin-react(no-unknown-property): Unknown property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div accesskey="bar"></div>;
· ─────────
╰────
help: Use 'accessKey' instead

eslint-plugin-react(no-unknown-property): Unknown property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div onclick="bar"></div>;
· ───────
╰────
help: Use 'onClick' instead

eslint-plugin-react(no-unknown-property): Unknown property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div onmousedown="bar"></div>;
· ───────────
╰────
help: Use 'onMouseDown' instead

eslint-plugin-react(no-unknown-property): Unknown property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div onMousedown="bar"></div>;
· ───────────
╰────
help: Use 'onMouseDown' instead

eslint-plugin-react(no-unknown-property): Unknown property found
╭─[no_unknown_property.tsx:1:1]
1 │ <use xlink:href="bar" />;
· ──────────
╰────
help: Use 'xlinkHref' instead

eslint-plugin-react(no-unknown-property): Unknown property found
╭─[no_unknown_property.tsx:1:1]
1 │ <rect clip-path="bar" />;
· ─────────
╰────
help: Use 'clipPath' instead

eslint-plugin-react(no-unknown-property): Unknown property found
╭─[no_unknown_property.tsx:1:1]
1 │ <script crossorigin nomodule />
· ───────────
╰────
help: Use 'crossOrigin' instead

eslint-plugin-react(no-unknown-property): Unknown property found
╭─[no_unknown_property.tsx:1:1]
1 │ <script crossorigin nomodule />
· ────────
╰────
help: Use 'noModule' instead

eslint-plugin-react(no-unknown-property): Unknown property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div crossorigin />
· ───────────
╰────
help: Use 'crossOrigin' instead

eslint-plugin-react(no-unknown-property): Invalid property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div crossOrigin />
· ───────────
╰────
help: Property 'crossOrigin' is only allowed on: script, audio, link, image, video, img

eslint-plugin-react(no-unknown-property): Invalid property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div as="audio" />
· ──
╰────
help: Property 'as' is only allowed on: link

eslint-plugin-react(no-unknown-property): Invalid property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div onAbort={this.abort} onDurationChange={this.durationChange} onEmptied={this.emptied} onEnded={this.end} onResize={this.resize} onError={this.error} />
· ───────
╰────
help: Property 'onAbort' is only allowed on: video, audio

eslint-plugin-react(no-unknown-property): Invalid property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div onAbort={this.abort} onDurationChange={this.durationChange} onEmptied={this.emptied} onEnded={this.end} onResize={this.resize} onError={this.error} />
· ────────────────
╰────
help: Property 'onDurationChange' is only allowed on: video, audio

eslint-plugin-react(no-unknown-property): Invalid property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div onAbort={this.abort} onDurationChange={this.durationChange} onEmptied={this.emptied} onEnded={this.end} onResize={this.resize} onError={this.error} />
· ─────────
╰────
help: Property 'onEmptied' is only allowed on: video, audio

eslint-plugin-react(no-unknown-property): Invalid property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div onAbort={this.abort} onDurationChange={this.durationChange} onEmptied={this.emptied} onEnded={this.end} onResize={this.resize} onError={this.error} />
· ───────
╰────
help: Property 'onEnded' is only allowed on: video, audio

eslint-plugin-react(no-unknown-property): Invalid property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div onAbort={this.abort} onDurationChange={this.durationChange} onEmptied={this.emptied} onEnded={this.end} onResize={this.resize} onError={this.error} />
· ────────
╰────
help: Property 'onResize' is only allowed on: video, audio

eslint-plugin-react(no-unknown-property): Invalid property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div onAbort={this.abort} onDurationChange={this.durationChange} onEmptied={this.emptied} onEnded={this.end} onResize={this.resize} onError={this.error} />
· ───────
╰────
help: Property 'onError' is only allowed on: source, link, script, audio, video, iframe, img, picture

eslint-plugin-react(no-unknown-property): Invalid property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div onLoad={this.load} />
· ──────
╰────
help: Property 'onLoad' is only allowed on: source, iframe, picture, script, object, img, link

eslint-plugin-react(no-unknown-property): Invalid property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div fill="pink" />
· ────
╰────
help: Property 'fill' is only allowed on: rect, ellipse, textPath, animateMotion, tref, animate, marker, svg, animateColor, use, symbol, polygon, text, circle, polyline, path, altGlyph, tspan,
set, g, line, animateTransform, mask

eslint-plugin-react(no-unknown-property): Invalid property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div controls={this.controls} loop={true} muted={false} src={this.videoSrc} playsInline={true} allowFullScreen></div>
· ────────
╰────
help: Property 'controls' is only allowed on: video, audio

eslint-plugin-react(no-unknown-property): Invalid property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div controls={this.controls} loop={true} muted={false} src={this.videoSrc} playsInline={true} allowFullScreen></div>
· ────
╰────
help: Property 'loop' is only allowed on: video, audio

eslint-plugin-react(no-unknown-property): Invalid property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div controls={this.controls} loop={true} muted={false} src={this.videoSrc} playsInline={true} allowFullScreen></div>
· ─────
╰────
help: Property 'muted' is only allowed on: video, audio

eslint-plugin-react(no-unknown-property): Invalid property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div controls={this.controls} loop={true} muted={false} src={this.videoSrc} playsInline={true} allowFullScreen></div>
· ───────────
╰────
help: Property 'playsInline' is only allowed on: video

eslint-plugin-react(no-unknown-property): Invalid property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div controls={this.controls} loop={true} muted={false} src={this.videoSrc} playsInline={true} allowFullScreen></div>
· ───────────────
╰────
help: Property 'allowFullScreen' is only allowed on: iframe, video

eslint-plugin-react(no-unknown-property): Invalid property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div download="foo" />
· ────────
╰────
help: Property 'download' is only allowed on: area, a

eslint-plugin-react(no-unknown-property): Invalid property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div imageSrcSet="someImageSrcSet" />
· ───────────
╰────
help: Property 'imageSrcSet' is only allowed on: link

eslint-plugin-react(no-unknown-property): Invalid property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div imageSizes="someImageSizes" />
· ──────────
╰────
help: Property 'imageSizes' is only allowed on: link

eslint-plugin-react(no-unknown-property): Unknown property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div data-xml-anything="invalid" />
· ─────────────────
╰────
help: Remove unknown property

eslint-plugin-react(no-unknown-property): React does not recognize data-* props with uppercase characters on a DOM element
╭─[no_unknown_property.tsx:1:1]
1 │ <div data-testID="bar" data-under_sCoRe="bar" />;
· ───────────
╰────
help: Use 'data-testid' instead

eslint-plugin-react(no-unknown-property): React does not recognize data-* props with uppercase characters on a DOM element
╭─[no_unknown_property.tsx:1:1]
1 │ <div data-testID="bar" data-under_sCoRe="bar" />;
· ────────────────
╰────
help: Use 'data-under_score' instead

eslint-plugin-react(no-unknown-property): Invalid property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div abbr="abbr" />
· ────
╰────
help: Property 'abbr' is only allowed on: td, th

eslint-plugin-react(no-unknown-property): Invalid property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div webkitDirectory="" />
· ───────────────
╰────
help: Property 'webkitDirectory' is only allowed on: input

eslint-plugin-react(no-unknown-property): Invalid property found
╭─[no_unknown_property.tsx:1:1]
1 │ <div webkitdirectory="" />
· ───────────────
╰────
help: Property 'webkitdirectory' is only allowed on: input

eslint-plugin-react(no-unknown-property): Unknown property found
╭─[no_unknown_property.tsx:1:1]
1 │
2 │ <div className="App" data-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash-crash:c="customValue">
· ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
3 │ Hello, world!
╰────
help: Remove unknown property


9 changes: 9 additions & 0 deletions crates/oxc_linter/src/utils/react.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ pub fn get_prop_value<'a, 'b>(item: &'b JSXAttributeItem<'a>) -> Option<&'b JSXA
}
}

pub fn get_prop_name(item: &JSXAttributeName) -> String {
match item {
JSXAttributeName::NamespacedName(name) => {
format!("{}:{}", name.namespace.name.as_str(), name.property.name.as_str())
}
JSXAttributeName::Identifier(ident) => ident.name.to_string(),
}
}

pub fn get_literal_prop_value<'a>(item: &'a JSXAttributeItem<'_>) -> Option<&'a str> {
get_prop_value(item).and_then(|v| {
if let JSXAttributeValue::StringLiteral(s) = v {
Expand Down

0 comments on commit c6eb519

Please sign in to comment.