-
-
Notifications
You must be signed in to change notification settings - Fork 728
feat(lint): implement no-unknown-property
from eslint react
#7774
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: next
Are you sure you want to change the base?
Conversation
π¦ Changeset detectedLatest commit: 22e95d7 The changes in this PR will be included in the next version bump. This PR includes changesets to release 14 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
no-unknown-property
from eslint react
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A good first draft :)
Also, this should implement the ignore
option for the css rule.
crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs
Outdated
Show resolved
Hide resolved
crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs
Outdated
Show resolved
Hide resolved
crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs
Outdated
Show resolved
Hide resolved
crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs
Outdated
Show resolved
Hide resolved
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/ignore/valid.options.json
Show resolved
Hide resolved
crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs
Outdated
Show resolved
Hide resolved
crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs
Outdated
Show resolved
Hide resolved
.changeset/small-words-show.md
Outdated
'@biomejs/biome': minor | ||
--- | ||
|
||
added the new rule [`no-unknown-property`](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-unknown-property.md) from react-rules |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should also contain examples of valid/invalid code. It also should mention ignore
getting added to the css rule (maybe in another changeset).
added the new rule [`no-unknown-property`](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-unknown-property.md) from react-rules | |
Added the new rule [`noUnknownProperty`](use biome link here) from react-rules |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, i need to check for css rule
CodSpeed Performance ReportMerging #7774 will not alter performanceComparing Summary
Footnotes
|
- Remove to_lowercase from most places - Added react domain to the rule - Removed regex and replaced with custom if checks - Merged two constant arrays to avoid allocations - remove TextRange from State, to use ctx.query() - Used standard messages for diagnostics
WalkthroughThis PR adds a nursery lint rule Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touchesβ Failed checks (1 warning)
β Passed checks (4 passed)
β¨ Finishing touches
π§ͺ Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
β»οΈ Duplicate comments (1)
.changeset/small-words-show.md (1)
5-5
: Tighten the changeset copy, add examples, and fix the link.
- Use past tense, proper naming (βeslintβpluginβreactβ), and end sentences with a period.
- Add tiny valid/invalid examples.
- Point to the correct rule page; the current slug likely collides with the CSS rule page. Update once the docs page exists.
Apply this diff:
-Added the new rule [`no-unknown-property`](https://biomejs.dev/linter/rules/no-unknown-property/) from eslint react +#### Lint rules. + +Added the new nursery rule [`noUnknownProperty`](<update-with-correct-docs-url>) ported from `eslint-plugin-react`. + +Examples: + +```jsx +/* valid */ +<App class="foo" /><div data-foo="bar" /> +``` + +```jsx +/* invalid */ +<div class="foo" /><div data-testID="bar" /> +```As per coding guidelines.
π§Ή Nitpick comments (10)
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/ignore/valid.jsx (1)
1-6
: Nice focused βignoreβ case.Consider adding a sibling negative case (property not on the ignore list) to prove the rule still fires.
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/lowercase/invalid.jsx (1)
1-4
: Confirm parity on custom components.Do we intend
requireDataLowercase
to apply to custom components like<App />
? ESLintβs rule often limits DOMβprop checks to host elements. If we want parity, scope this to intrinsic elements; if we want stricter Biome behaviour, keep as is and document it.crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/valid.jsx (1)
1-204
: Big omnibus βvalidβ test β consider splitting by theme.Smaller themed fixtures (events, SVG, boolean attrs, data/aria, legacy attrs) make diffs and failures easier to triage. Also keep the ReactΒ 19 popover block gated in a dedicated future test.
Quick sweep for potential tagβspecific mismatches (e.g.
allowFullScreen
on nonβiframe) would be good to doubleβcheck against the ruleβs allowedβtags map.crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/invalid.jsx (2)
21-38
: Add popover/onBeforeToggle cases to lock React 19 supportPlease add invalid/valid cases for popover API:
- Attributes: popover, popovertarget, popovertargetaction.
- Events: onBeforeToggle/onToggle on allowed tags.
This prevents regressions and aligns with your commented code in the rule.
1-3
: Consider a companion valid case for custom elementsAdd a valid fixture using a custom element (e.g., ) to assert the rule skips custom elements and tags with an is attribute. This documents intent.
crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs (5)
1065-1077
: Avoid avoidable allocations for attribute namesname/value_token().text_trimmed().to_string() and the manual namespace concatenation allocate on every attribute. Use TokenText and Cow to allocate only for namespaced cases.
@@ - let node_name = match node.name().ok()? { - AnyJsxAttributeName::JsxName(name) => { - name.value_token().ok()?.text_trimmed().to_string() - } - AnyJsxAttributeName::JsxNamespaceName(name) => { - let namespace = name.namespace().ok()?.value_token().ok()?; - let name = &name.name().ok()?.value_token().ok()?; - // There could be better way, but i couldn't extract namespaced attributes - // For e.g xlink:href - // without manually concatenating with ':' - namespace.text_trimmed().to_string() + ":" + name.text_trimmed() - } - }; + use std::borrow::Cow; + let node_name: Cow<'_, str> = match node.name().ok()? { + AnyJsxAttributeName::JsxName(name) => { + Cow::Borrowed(name.value_token().ok()?.text_trimmed()) + } + AnyJsxAttributeName::JsxNamespaceName(name) => { + let ns = name.namespace().ok()?.value_token().ok()?.text_trimmed(); + let local = name.name().ok()?.value_token().ok()?.text_trimmed(); + Cow::Owned(format!("{ns}:{local}")) + } + }; @@ - if options.ignore.contains(&node_name) { + if options.ignore.iter().any(|s| s == &*node_name) { return None; } @@ - let name = normalize_attribute_case(&node_name); + let name = normalize_attribute_case(&node_name);Also applies to: 1079-1081
1009-1014
: Simplify tag_name_has_dot signatureThis always returns Some(..). Make it return bool and drop ? at call sites; avoids needless Option churn.
-fn tag_name_has_dot(node: &AnyJsxElement) -> Option<bool> { - Some(matches!( - node.name().ok()?, - AnyJsxElementName::JsxMemberName(_) - )) -} +fn tag_name_has_dot(node: &AnyJsxElement) -> bool { + matches!(node.name().ok(), Ok(AnyJsxElementName::JsxMemberName(_))) +} @@ - if tag_name_has_dot(&element)? { + if tag_name_has_dot(&element) { return None; }
1167-1183
: Minor copy tweakThe diagnostic says βfor React componentsβ, but this rule validates DOM elements after gating out components. Suggest βin JSXβ to avoid confusion.
- "Use '"{standard_name}"' instead of '"{name}"' for React components." + "Use '"{standard_name}"' instead of '"{name}"' in JSX."
1484-1510
: Use caseβinsensitive check for dataβ only after the prefix*Current has_uppercase(name) flags any uppercase anywhere, which is fine, but the option intent is about the part after data-. Consider restricting the scan to the suffix to keep the message crisp for odd inputs.
- if options.require_data_lowercase && has_uppercase(&name) { + if options.require_data_lowercase { + let suffix = &name["data-".len()..]; + if has_uppercase(suffix) { return Some(NoUnknownPropertyDiagnostic::DataLowercaseRequired { name: name.to_string(), lowercase_name: name.to_lowercase(), }); - } + } + }
226-231
: Keys are sorted β guard test is defensive, not criticalThe
ATTRIBUTE_TAGS_MAP
keys are currently sorted and the code is correct. However, the suggestion to add a guard test is reasonable: without one, future edits could accidentally break sort order without triggering a compile-time error, andbinary_search_by_key
would silently fail at runtime.Consider adding a simple unit test at the end of the file to assert the map remains sorted:
#[cfg(test)] mod tests { use super::*; #[test] fn attribute_tags_map_is_sorted() { let keys: Vec<_> = ATTRIBUTE_TAGS_MAP.iter().map(|(k, _)| k).collect(); let mut sorted = keys.clone(); sorted.sort(); assert_eq!(keys, sorted, "ATTRIBUTE_TAGS_MAP keys must remain sorted for binary_search_by_key"); } }
π Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
β Files ignored due to path filters (11)
crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs
is excluded by!**/migrate/eslint_any_rule_to_biome.rs
and included by**
crates/biome_configuration/src/analyzer/linter/rules.rs
is excluded by!**/rules.rs
and included by**
crates/biome_configuration/src/generated/domain_selector.rs
is excluded by!**/generated/**
,!**/generated/**
and included by**
crates/biome_diagnostics_categories/src/categories.rs
is excluded by!**/categories.rs
and included by**
crates/biome_js_analyze/src/lint/nursery.rs
is excluded by!**/nursery.rs
and included by**
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/ignore/valid.jsx.snap
is excluded by!**/*.snap
and included by**
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/invalid.jsx.snap
is excluded by!**/*.snap
and included by**
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/lowercase/invalid.jsx.snap
is excluded by!**/*.snap
and included by**
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/valid.jsx.snap
is excluded by!**/*.snap
and included by**
packages/@biomejs/backend-jsonrpc/src/workspace.ts
is excluded by!**/backend-jsonrpc/src/workspace.ts
and included by**
packages/@biomejs/biome/configuration_schema.json
is excluded by!**/configuration_schema.json
and included by**
π Files selected for processing (10)
.changeset/small-words-show.md
(1 hunks)crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs
(1 hunks)crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/ignore/valid.jsx
(1 hunks)crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/ignore/valid.options.json
(1 hunks)crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/invalid.jsx
(1 hunks)crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/lowercase/invalid.jsx
(1 hunks)crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/lowercase/invalid.options.json
(1 hunks)crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/valid.jsx
(1 hunks)crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/valid.package.json
(1 hunks)crates/biome_rule_options/src/no_unknown_property.rs
(1 hunks)
π§° Additional context used
π Path-based instructions (5)
crates/biome_*/**
π CodeRabbit inference engine (CLAUDE.md)
Place core crates under /crates/biome_*/
Files:
crates/biome_rule_options/src/no_unknown_property.rs
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/valid.jsx
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/ignore/valid.options.json
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/lowercase/invalid.options.json
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/invalid.jsx
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/ignore/valid.jsx
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/valid.package.json
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/lowercase/invalid.jsx
crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs
**/*.rs
π CodeRabbit inference engine (CONTRIBUTING.md)
**/*.rs
: Format Rust files before committing (e.g., viajust f
which formats Rust)
Document rules, assists, and options with inline rustdoc in source
Files:
crates/biome_rule_options/src/no_unknown_property.rs
crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**
π CodeRabbit inference engine (CLAUDE.md)
Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}
Files:
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/valid.jsx
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/ignore/valid.options.json
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/lowercase/invalid.options.json
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/invalid.jsx
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/ignore/valid.jsx
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/valid.package.json
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/lowercase/invalid.jsx
crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs
**/tests/**
π CodeRabbit inference engine (CLAUDE.md)
Place test files under a tests/ directory in each crate
Files:
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/valid.jsx
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/ignore/valid.options.json
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/lowercase/invalid.options.json
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/invalid.jsx
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/ignore/valid.jsx
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/valid.package.json
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/lowercase/invalid.jsx
.changeset/*.md
π CodeRabbit inference engine (CONTRIBUTING.md)
.changeset/*.md
: In changesets, only use #### or ##### headers; other header levels are not allowed
Changesets should cover user-facing changes only; internal changes do not need changesets
Use past tense for what you did and present tense for current Biome behavior in changesets
When fixing a bug in a changeset, start with an issue link (e.g., βFixed #1234: β¦β)
When referencing a rule or assist in a changeset, include a link to its page on the website
Include code blocks in changesets when applicable to illustrate changes
End every sentence in a changeset with a period
Files:
.changeset/small-words-show.md
π§ Learnings (6)
π Learning: 2025-10-15T09:20:19.139Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:20:19.139Z
Learning: Applies to crates/biome_analyze/crates/biome_rule_options/lib/**/*.rs : For options types, derive Serialize, Deserialize, Deserializable (and JsonSchema under the schema feature) and use #[serde(rename_all="camelCase", deny_unknown_fields, default)] with skip_serializing_if where appropriate
Applied to files:
crates/biome_rule_options/src/no_unknown_property.rs
π Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/tests/specs/**/options.json : Use options.json files colocated with test inputs to override formatting options for all files in that folder
Applied to files:
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/ignore/valid.options.json
π Learning: 2025-08-05T14:43:29.581Z
Learnt from: dyc3
PR: biomejs/biome#7081
File: packages/@biomejs/biome/configuration_schema.json:7765-7781
Timestamp: 2025-08-05T14:43:29.581Z
Learning: The file `packages/biomejs/biome/configuration_schema.json` is auto-generated and should not be manually edited or reviewed for schema issues; any changes should be made at the code generation source.
Applied to files:
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/ignore/valid.options.json
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/lowercase/invalid.options.json
π Learning: 2025-10-15T09:20:19.139Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:20:19.139Z
Learning: Applies to crates/biome_analyze/**/tests/specs/**/*.{js,jsx,ts,tsx} : Place snapshot test cases under tests/specs/<group>/<ruleName>/ with files typically prefixed by invalid/ or valid/
Applied to files:
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/lowercase/invalid.options.json
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/ignore/valid.jsx
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/valid.package.json
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/lowercase/invalid.jsx
π Learning: 2025-10-15T09:20:19.139Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:20:19.139Z
Learning: A ruleβs diagnostic should explain what the error is, why itβs triggered, and what to do (ideally via a code action or a note)
Applied to files:
crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs
π Learning: 2025-10-15T09:21:24.116Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_diagnostics/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:21:24.116Z
Learning: A diagnostic must explain why something went wrong, using log advices and links to documentation when helpful
Applied to files:
crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs
𧬠Code graph analysis (2)
crates/biome_rule_options/src/no_unknown_property.rs (1)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
NoUnknownPropertyOptions
(8160-8163)
crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs (2)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (2)
RuleDomain
(927-935)NoUnknownPropertyOptions
(8160-8163)crates/biome_rowan/src/ast/mod.rs (1)
cast_ref
(142-151)
π Additional comments (4)
crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/ignore/valid.options.json (1)
1-16
: No issues identified; convention verified.The per-file naming pattern (
<basename>.options.json
) is indeed the established convention across analyser specsβover 20 examples confirm this, including othernoUnknownProperty
subfolder tests. The review comment is accurate.crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/valid.package.json (1)
1-6
: Remove or document the React 16.4.0 fixture as a placeholder.The rule currently does not consult React versions (no version gating logic exists in the implementation). The commented-out
POPOVER_API_PROPS
constant suggests this is planned. Either drop the fixture file to avoid confusion, or add a comment documenting that it's a placeholder for future version-gated testing.crates/biome_js_analyze/tests/specs/nursery/noUnknownProperty/lowercase/invalid.options.json (1)
1-17
: Verified: Options structure and JSON serialisation are correct.The struct at
crates/biome_rule_options/src/no_unknown_property.rs
has the expected serde attributes:#[serde(rename_all = "camelCase", deny_unknown_fields, default)]
with proper derives (Serialize
,Deserialize
,Deserializable
,JsonSchema
). The fieldrequire_data_lowercase
correctly serialises to"requireDataLowercase"
via camelCase conversion, matching the test JSON precisely. Pattern confirmed across 174.options.json
files in the specs directory.crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs (1)
56-87
: Code already correctly configuredβno changes neededThe struct in
crates/biome_rule_options/src/no_unknown_property.rs
already has the proper serde setup:
#[serde(rename_all = "camelCase", deny_unknown_fields, default)]
is present β- Rust field
require_data_lowercase
(snake_case) correctly maps to JSONrequireDataLowercase
via the bulk rename- Documentation uses the correct camelCase field names
- Field
ignore
properly uses#[serde(skip_serializing_if = "Vec::is_empty")]
This follows the established pattern for options types in the codebase. The serde rename mechanism is already working as intended.
"onAbort", | ||
"onAbortCapture", | ||
"onAnimationEnd", | ||
"onAnimationEndCapture", | ||
"onAnimationIteration", | ||
"onAnimationStart", | ||
"onAnimationStartCapture", | ||
"onAuxClick", | ||
"onAuxClickCapture", | ||
"onBeforeInput", | ||
"onBeforeInputCapture", | ||
"onbeforetoggle", | ||
"onBlur", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π οΈ Refactor suggestion | π Major
Incorrect/ambiguous event casing: include onBeforeToggle (React 19)
DOM_PROPERTY_NAMES contains onToggle and ontoggle, but only onbeforetoggle (lowercase) for the preβtoggle event. Canonical React prop is onBeforeToggle. Without it, kebab/incorrect casing wonβt be autoβfixed.
Proposed fix:
- Add "onBeforeToggle".
- Keep "onbeforetoggle" only if you intend to recognise legacy HTML attribute casing; otherwise remove it to prefer camelCase.
@@
- "onbeforetoggle",
+ "onBeforeToggle",
@@
- "ontoggle",
+ "onToggle",
Add tests in specs for onbeforetoggle β onBeforeToggle and ontoggle β onToggle on valid/invalid tags.
Also applies to: 846-848
π€ Prompt for AI Agents
In crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs around lines
701 to 713, the list of DOM property/event names is missing the canonical React
camelCase "onBeforeToggle" (it currently only contains the lowercase
"onbeforetoggle"), which prevents auto-fixing incorrect casing; add
"onBeforeToggle" to the array (you may keep "onbeforetoggle" only if you
intentionally want to recognize legacy HTML attribute casing), and update the
test specs around lines 846-848 to include cases asserting that "onbeforetoggle"
β "onBeforeToggle" and "ontoggle" β "onToggle" are handled as valid/invalid
where appropriate.
pub struct NoUnknownPropertyOptions { | ||
pub require_data_lowercase: bool, | ||
#[serde(skip_serializing_if = "Vec::is_empty")] | ||
pub ignore: Vec<String>, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π§© Analysis chain
Add rustdoc and check schema optionality for ignore
.
- Please document the struct and fields (what each option does, defaults).
- Ensure the generated JSON schema doesnβt make
ignore
required; TS shows it as required today.
Proposed doc comments:
#[serde(rename_all = "camelCase", deny_unknown_fields, default)]
pub struct NoUnknownPropertyOptions {
- pub require_data_lowercase: bool,
+ /// Enforce lower-case `data-*` attribute names when true. Default: false.
+ pub require_data_lowercase: bool,
#[serde(skip_serializing_if = "Vec::is_empty")]
- pub ignore: Vec<String>,
+ /// Additional attribute names to ignore (case-sensitive). Default: [].
+ pub ignore: Vec<String>,
}
Verify schema/TS:
π Script executed:
#!/bin/bash
# After codegen, check required fields under noUnknownProperty
jq -r '
.. | objects | select(has("title") and .title=="noUnknownProperty") |
.allOf[]?.then?.properties? // empty
' packages/@biomejs/biome/configuration_schema.json
rg -n 'interface NoUnknownPropertyOptions' packages/@biomejs/backend-jsonrpc/src/workspace.ts -n -A3
Length of output: 117
π Script executed:
#!/bin/bash
# Check current state of the Rust file to see if rustdoc is present
cat crates/biome_rule_options/src/no_unknown_property.rs
Length of output: 456
Add rustdoc and fix schema optionality for ignore
.
Confirmed: the struct lacks documentation, and the TypeScript interface incorrectly marks ignore
as required. The #[serde(skip_serializing_if = "Vec::is_empty")]
attribute means it should be optional in the schema.
Implement the proposed rustdoc additions and verify the codegen pipeline regenerates the TypeScript interface with ignore?: string[]
.
π€ Prompt for AI Agents
In crates/biome_rule_options/src/no_unknown_property.rs around lines 6β10, add
rustdoc comments describing the struct and its fields, and make the `ignore`
field optional for schema generation by changing its type to Option<Vec<String>>
and updating serde attributes to use a default and skip serialization when None
(e.g. add #[serde(default, skip_serializing_if = "Option::is_none")]). After
updating the doc comments and the field type/serde attrs, run the codegen
pipeline to regenerate the TypeScript interface so it emits `ignore?: string[]`.
@dyc3 hey thank you for your comments, i have worked on your suggestions, and I think they are resolved. I believe these are the things I need to work on next
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
β»οΈ Duplicate comments (1)
crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs (1)
713-714
: Event casing: add onBeforeToggle; drop ontoggle; prefer camelCase suggestionsCurrent list includes lowercase
"onbeforetoggle"
and"ontoggle"
, but misses the canonical React props"onBeforeToggle"
and (already present)"onToggle"
. As written,ontoggle
/onbeforetoggle
will be accepted and wonβt autoβfix to camelCase.- "onbeforetoggle", + "onBeforeToggle", @@ - "ontoggle", + // Intentionally omit lowercase "ontoggle" so it maps to "onToggle"Add tests asserting:
<div onbeforetoggle={() => {}} />
β diagnostic suggestingonBeforeToggle
<div ontoggle={() => {}} />
β diagnostic suggestingonToggle
This keeps behaviour aligned with React 19βs events and improves autoβfix.
Also applies to: 847-848
π§Ή Nitpick comments (3)
crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs (3)
1065-1077
: Avoid unnecessary allocations for attribute names; make ignore check caseβinsensitiveYou allocate for every attribute (
to_string
, manual concat). We can avoid allocations in the common case and only allocate for namespaced attributes, while matchingignore
without allocating.+use std::borrow::Cow; @@ - let node_name = match node.name().ok()? { - AnyJsxAttributeName::JsxName(name) => { - name.value_token().ok()?.text_trimmed().to_string() - } - AnyJsxAttributeName::JsxNamespaceName(name) => { - let namespace = name.namespace().ok()?.value_token().ok()?; - let name = &name.name().ok()?.value_token().ok()?; - // There could be better way, but i couldn't extract namespaced attributes - // For e.g xlink:href - // without manually concatenating with ':' - namespace.text_trimmed().to_string() + ":" + name.text_trimmed() - } - }; + let node_name_text: Cow<'_, str> = match node.name().ok()? { + AnyJsxAttributeName::JsxName(name) => { + Cow::Borrowed(name.value_token().ok()?.text_trimmed()) + } + AnyJsxAttributeName::JsxNamespaceName(name) => { + let ns = name.namespace().ok()?.value_token().ok()?.text_trimmed(); + let nm = name.name().ok()?.value_token().ok()?.text_trimmed(); + Cow::Owned(format!("{ns}:{nm}")) + } + }; @@ - if options.ignore.contains(&node_name) { + if options + .ignore + .iter() + .any(|s| s.eq_ignore_ascii_case(node_name_text.as_ref())) + { return None; } @@ - let name = normalize_attribute_case(&node_name); + let name = normalize_attribute_case(node_name_text.as_ref());This removes hotβpath allocations and makes
ignore
friendlier. Ifignore
is meant to be caseβsensitive, dropeq_ignore_ascii_case
.Also applies to: 1079-1084
972-973
: Redundantuse
inside function
use biome_string_case::StrOnlyExtension;
is already imported at the module level. Remove the inneruse
.- use biome_string_case::StrOnlyExtension;
1169-1177
: Offer quickβfixes in diagnostics (rename)You already tell users what to do; wire in code actions to autoβrename to
standard_name
and lowerβcasedata-*
attributes.Happy to sketch the
.action
plumbing if useful.Also applies to: 1205-1213
π Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
π Files selected for processing (1)
crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs
(1 hunks)
π§° Additional context used
π Path-based instructions (3)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**
π CodeRabbit inference engine (CLAUDE.md)
Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}
Files:
crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs
crates/biome_*/**
π CodeRabbit inference engine (CLAUDE.md)
Place core crates under /crates/biome_*/
Files:
crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs
**/*.rs
π CodeRabbit inference engine (CONTRIBUTING.md)
**/*.rs
: Format Rust files before committing (e.g., viajust f
which formats Rust)
Document rules, assists, and options with inline rustdoc in source
Files:
crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs
π§ Learnings (2)
π Learning: 2025-10-15T09:20:19.139Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:20:19.139Z
Learning: A ruleβs diagnostic should explain what the error is, why itβs triggered, and what to do (ideally via a code action or a note)
Applied to files:
crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs
π Learning: 2025-10-15T09:21:24.116Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_diagnostics/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:21:24.116Z
Learning: A diagnostic must explain why something went wrong, using log advices and links to documentation when helpful
Applied to files:
crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs
𧬠Code graph analysis (1)
crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs (3)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (2)
RuleDomain
(927-935)NoUnknownPropertyOptions
(8160-8163)crates/biome_analyze/src/rule.rs (4)
domains
(625-628)sources
(610-613)same
(246-251)recommended
(595-598)crates/biome_rowan/src/ast/mod.rs (1)
cast_ref
(142-151)
β° Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: triage
- GitHub Check: Validate PR title
π Additional comments (3)
crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs (3)
73-89
: No issues foundβserde mapping is correctly configuredThe
NoUnknownPropertyOptions
struct already includes#[serde(rename_all = "camelCase", deny_unknown_fields, default)]
, which ensures the Rust fieldrequire_data_lowercase
serialises asrequireDataLowercase
in JSON, matching the documentation. All coding guidelines are satisfied.
95-100
: The review comment is incorrectβbothRuleSource
variants are valid and activeBoth
EslintReactX
andEslintReactXyz
are legitimate, properly-documented variants in theRuleSource
enum (lines 125 and 127 ofcrates/biome_analyze/src/rule.rs
). They represent distinct ESLint React plugins with different outputs in the Display trait:
EslintReactX
displays as "eslint-plugin-react-x"EslintReactXyz
displays as "@eslint-react/eslint-plugin"The code compiles and is correct as written. The suggestion to consolidate to a single
EslintReact
source would lose important provenance information about which tool each rule originates from.Likely an incorrect or invalid review comment.
114-225
: All constants are correctly sortedβno action needed.The verification confirms that
ATTRIBUTE_TAGS_MAP
,DOM_ATTRIBUTE_NAMES
, andSVGDOM_ATTRIBUTE_NAMES
are all properly sorted by their first keys. Binary search will work as expected; the code is sound.
"popover", | ||
"popovertarget", | ||
"popovertargetaction", | ||
"preserveAlpha", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Popover API props should be camelCase (React 19)
"popovertarget"
and "popovertargetaction"
should be "popoverTarget"
and "popoverTargetAction"
so that lowercase inputs get mapped and suggested correctly.
- "popovertarget",
- "popovertargetaction",
+ "popoverTarget",
+ "popoverTargetAction",
Consider adding tests for:
<button popovertarget="id" />
β suggestpopoverTarget
<button popovertargetaction="show" />
β suggestpopoverTargetAction
π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
"popover", | |
"popovertarget", | |
"popovertargetaction", | |
"preserveAlpha", | |
"popover", | |
"popoverTarget", | |
"popoverTargetAction", | |
"preserveAlpha", |
π€ Prompt for AI Agents
In crates/biome_js_analyze/src/lint/nursery/no_unknown_property.rs around lines
877-880, the list of Popover API props includes "popovertarget" and
"popovertargetaction" in lowercase; update them to the camelCase versions
"popoverTarget" and "popoverTargetAction" so the linter maps lowercase inputs to
the correct suggestions, and add unit tests covering <button popovertarget="id"
/> β suggest popoverTarget and <button popovertargetaction="show" /> β suggest
popoverTargetAction to verify behavior.
name.value_token().ok()?.text_trimmed().to_string() | ||
} | ||
AnyJsxAttributeName::JsxNamespaceName(name) => { | ||
let namespace = name.namespace().ok()?.value_token().ok()?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dyc3 And this issue as well, I couldn't figure out the method to have attributes with colon , something like xmlns:href
from the element, and I had to manually concatenate :
and convert to String
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could put namespaced attributes into a different map, and that would let you avoid the string allocation.
/// | ||
pub NoUnknownProperty { | ||
version: "next", | ||
name: "noUnknownProperty", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking that this actually isn't a good name for the rule. The CSS concept for "property" and react's concept for "property" are rather different.
We already call react props "Jsx Attribute". I think it would make more sense to name this rule noUnknownAttribute
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Iβm not sure if I actually read it somewhere or if Iβm just misremembering, but I recall reading that we should keep the rule name the same for easier migration ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I did say previously that we should keep the same rule name. I have taken a closer look at what this rule does, and what the css rule of the same name does, and have changed my opinion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I will change the name then
#[serde(rename_all = "camelCase", deny_unknown_fields, default)] | ||
pub struct NoUnknownPropertyOptions {} | ||
pub struct NoUnknownPropertyOptions { | ||
pub require_data_lowercase: bool, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove require_data_lowercase
and match the default behavior of the source rule (which is true
iirc)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the default behavior is set to false
.
https://eslint-react.xyz/docs/rules/dom-no-unknown-property#rule-options
But, I am not sure why do you want to remove it ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We discussed this in the original issue: #7657 (comment)
but I apologize for not making it more clear: we don't implement options without a clear use case for it. The ignore option is fine, because it allows users an immediate escape hatch, and its trivial to implement. It's a mistake we have experienced with other rules.
You are right that the source rule has the default for this as false
. Idk where I read that it was true
from because I can't find it now. The more important thing is that we follow the defaults of the source rule as much as it makes sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dyc3 so, I should keep the ignore option ? Sorry, I just wanted to confirm
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, keep ignore
// "onBeforeToggle", | ||
// ]; | ||
|
||
const ATTRIBUTE_TAGS_MAP: &[(&str, &[&str])] = &[ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could use LazyLock
to let this be an actual HashMap
, or FxHashMap
. Not sure which would be best for this use case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dyc3 I will run benchmarks on this first and then let you know
name.value_token().ok()?.text_trimmed().to_string() | ||
} | ||
AnyJsxAttributeName::JsxNamespaceName(name) => { | ||
let namespace = name.namespace().ok()?.value_token().ok()?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could put namespaced attributes into a different map, and that would let you avoid the string allocation.
@@ -0,0 +1,5 @@ | |||
--- | |||
'@biomejs/biome': minor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'@biomejs/biome': minor | |
'@biomejs/biome': patch |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And should I open this PR against next
or main
branch ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New nursery rules can go in patch releases. You don't have to close this PR to change the base branch of it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure
Summary
This PR migrates the no_unknown_property rule from Eslint React. This closes #7657
However, my code is not ready for a good PR yet π . For some reasons, I am unable to test with the options although i have them inside options.json file. This code doesn't take care of the react version to handle some edge cases.
This PR may conflict with existing no-unknown-property rule from css, and may redirect to same url.
Test Plan
The tests are included according to the spec. Valid cases are inside valid.jsx and invalid cases in invalid.jsx. For valid and invalid cases with options, I have added them in different folder. For ignore options, the code is inside ignore folder and for requireDataLowerCase, they are stored inside lowercase folder.
Docs