Skip to content

feat(lint/js): add noReactStringRefs#9922

Open
dyc3 wants to merge 1 commit intomainfrom
dyc3/noReactStringRefs
Open

feat(lint/js): add noReactStringRefs#9922
dyc3 wants to merge 1 commit intomainfrom
dyc3/noReactStringRefs

Conversation

@dyc3
Copy link
Copy Markdown
Contributor

@dyc3 dyc3 commented Apr 11, 2026

Summary

This adds noReactStringRefs which is a port of https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-string-refs.md

Generated by gpt 5.4

Test Plan

snapshots

Docs

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 11, 2026

🦋 Changeset detected

Latest commit: cc98726

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@biomejs/biome Patch
@biomejs/cli-win32-x64 Patch
@biomejs/cli-win32-arm64 Patch
@biomejs/cli-darwin-x64 Patch
@biomejs/cli-darwin-arm64 Patch
@biomejs/cli-linux-x64 Patch
@biomejs/cli-linux-arm64 Patch
@biomejs/cli-linux-x64-musl Patch
@biomejs/cli-linux-arm64-musl Patch
@biomejs/wasm-web Patch
@biomejs/wasm-bundler Patch
@biomejs/wasm-nodejs Patch
@biomejs/backend-jsonrpc Patch

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

@github-actions github-actions bot added A-CLI Area: CLI A-Project Area: project A-Linter Area: linter L-JavaScript Language: JavaScript and super languages A-Diagnostic Area: diagnostocis labels Apr 11, 2026
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Apr 11, 2026

Merging this PR will not alter performance

✅ 58 untouched benchmarks
⏩ 196 skipped benchmarks1


Comparing dyc3/noReactStringRefs (cc98726) with main (9701a33)

Open in CodSpeed

Footnotes

  1. 196 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@dyc3 dyc3 force-pushed the dyc3/noReactStringRefs branch from 85810f3 to 302732f Compare April 11, 2026 14:25
Comment thread crates/biome_js_analyze/src/lint/nursery/no_react_string_refs.rs
@dyc3 dyc3 marked this pull request as ready for review April 11, 2026 14:26
@Netail
Copy link
Copy Markdown
Member

Netail commented Apr 11, 2026

Also, not sure if it's worth mentioning. But this is also caught by TS types, if the user makes use of Typescript & @types/react.

We could also add something similar for the style prop, like https://www.eslint-react.xyz/docs/rules/dom-no-string-style-prop (Same stuff really, caught by TS, but not JS)

@dyc3 dyc3 force-pushed the dyc3/noReactStringRefs branch from 302732f to 1002d10 Compare April 11, 2026 14:31
@dyc3 dyc3 requested review from a team April 11, 2026 14:31
@dyc3 dyc3 force-pushed the dyc3/noReactStringRefs branch from 1002d10 to 7a2be39 Compare April 11, 2026 14:32
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 11, 2026

Walkthrough

Adds a new nursery lint rule noReactStringRefs that detects legacy React string refs used as JSX ref attributes (string literals and template-literal chunks) and this.refs member expressions. The rule is registered for the React domain and enabled as a recommended warning, emits distinct diagnostics for attribute vs member-expression cases, and suppresses diagnostics when the project's react dependency resolves to >= 18.3.0. Also adds a semantic query union, rule state enum, rule options module and options struct, plus test fixtures (valid/invalid cases and a react@18.3.1 package.json).

Suggested reviewers

  • ematipico
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: adding a new lint rule noReactStringRefs to the JavaScript linter.
Description check ✅ Passed The description explains the motivation (porting an ESLint React plugin rule) and references the relevant documentation, relating directly to the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dyc3/noReactStringRefs

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a 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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/biome_js_analyze/src/lint/nursery/no_react_string_refs.rs`:
- Line 70: Replace the source parity marker for the rule by changing
RuleSource::EslintReact("no-string-refs").same() to use .inspired() instead of
.same(); locate the occurrence of RuleSource::EslintReact("no-string-refs") in
no_react_string_refs.rs and call .inspired() so the source reflects the
documented behavior divergence.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 98b3c36f-5ffc-44be-90ba-21c0d5881f81

📥 Commits

Reviewing files that changed from the base of the PR and between 9701a33 and 302732f.

⛔ Files ignored due to path filters (10)
  • 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_configuration/src/generated/linter_options_check.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/tests/specs/nursery/noReactStringRefs/invalid.jsx.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noReactStringRefs/react183/valid.jsx.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noReactStringRefs/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 (8)
  • .changeset/fifty-chicken-like.md
  • crates/biome_js_analyze/src/lint/nursery/no_react_string_refs.rs
  • crates/biome_js_analyze/tests/specs/nursery/noReactStringRefs/invalid.jsx
  • crates/biome_js_analyze/tests/specs/nursery/noReactStringRefs/react183/valid.jsx
  • crates/biome_js_analyze/tests/specs/nursery/noReactStringRefs/react183/valid.package.json
  • crates/biome_js_analyze/tests/specs/nursery/noReactStringRefs/valid.jsx
  • crates/biome_rule_options/src/lib.rs
  • crates/biome_rule_options/src/no_react_string_refs.rs

version: "next",
name: "noReactStringRefs",
language: "js",
sources: &[RuleSource::EslintReact("no-string-refs").same()],
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the local inconsistency between documented behaviour and source parity marker.
sed -n '20,75p' crates/biome_js_analyze/src/lint/nursery/no_react_string_refs.rs
rg -n 'EslintReact\("no-string-refs"\)\.(same|inspired)\(' crates/biome_js_analyze/src/lint

Repository: biomejs/biome

Length of output: 1622


🏁 Script executed:

sed -n '75,125p' crates/biome_js_analyze/src/lint/nursery/no_react_string_refs.rs

Repository: biomejs/biome

Length of output: 2049


🏁 Script executed:

sed -n '125,180p' crates/biome_js_analyze/src/lint/nursery/no_react_string_refs.rs

Repository: biomejs/biome

Length of output: 2409


Use .inspired() instead of .same() for source parity.

Line 65 marks this as behaviour-identical to ESLint, but the rule unconditionally flags template literal refs (lines 156–165), whereas ESLint's no-string-refs only does so via an optional setting. The documentation already calls this out (line 26), so the source marker should reflect the actual behaviour divergence.

Suggested fix
-        sources: &[RuleSource::EslintReact("no-string-refs").same()],
+        sources: &[RuleSource::EslintReact("no-string-refs").inspired()],
📝 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.

Suggested change
sources: &[RuleSource::EslintReact("no-string-refs").same()],
sources: &[RuleSource::EslintReact("no-string-refs").inspired()],
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_js_analyze/src/lint/nursery/no_react_string_refs.rs` at line 70,
Replace the source parity marker for the rule by changing
RuleSource::EslintReact("no-string-refs").same() to use .inspired() instead of
.same(); locate the occurrence of RuleSource::EslintReact("no-string-refs") in
no_react_string_refs.rs and call .inspired() so the source reflects the
documented behavior divergence.

@dyc3 dyc3 force-pushed the dyc3/noReactStringRefs branch from 7a2be39 to cc98726 Compare April 12, 2026 15:04
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
crates/biome_js_analyze/src/lint/nursery/no_react_string_refs.rs (1)

151-168: The template-literal helper comment and predicate disagree.

The comment says “only string chunks”, but the predicate currently accepts template elements too, so maintainers will trip over this later. Either tighten the predicate or simplify/comment it as “any template literal”.

One clear option (match current behaviour)
-/// Check if the attribute value is a string ref, which can be either a string literal or a template literal with only string chunks.
+/// Check if the attribute value is a string ref, either a string literal or any template literal.
 fn is_string_ref_value(value: &AnyJsxAttributeValue) -> bool {
@@
-                Some(AnyJsExpression::JsTemplateExpression(template)) => {
-                    template.elements().into_iter().any(|element| {
-                        matches!(
-                            element,
-                            AnyJsTemplateElement::JsTemplateChunkElement(_)
-                                | AnyJsTemplateElement::JsTemplateElement(_)
-                        )
-                    })
-                }
+                Some(AnyJsExpression::JsTemplateExpression(_)) => true,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_js_analyze/src/lint/nursery/no_react_string_refs.rs` around
lines 151 - 168, The comment on is_string_ref_value claims it should accept
"template literal with only string chunks" but the current predicate accepts
AnyJsTemplateElement::JsTemplateElement as well; update the predicate inside
is_string_ref_value to only treat template literals as string refs when every
element is a JsTemplateChunkElement (i.e., replace the current matches that
includes JsTemplateElement with a check that all template.elements() are
JsTemplateChunkElement), and adjust the comment to remain "only string chunks"
if you enforce this, or alternatively change the comment to "any template
literal" if you intend to keep the broader behavior—reference
is_string_ref_value, AnyJsTemplateElement::JsTemplateChunkElement, and
AnyJsTemplateElement::JsTemplateElement.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@crates/biome_js_analyze/src/lint/nursery/no_react_string_refs.rs`:
- Around line 151-168: The comment on is_string_ref_value claims it should
accept "template literal with only string chunks" but the current predicate
accepts AnyJsTemplateElement::JsTemplateElement as well; update the predicate
inside is_string_ref_value to only treat template literals as string refs when
every element is a JsTemplateChunkElement (i.e., replace the current matches
that includes JsTemplateElement with a check that all template.elements() are
JsTemplateChunkElement), and adjust the comment to remain "only string chunks"
if you enforce this, or alternatively change the comment to "any template
literal" if you intend to keep the broader behavior—reference
is_string_ref_value, AnyJsTemplateElement::JsTemplateChunkElement, and
AnyJsTemplateElement::JsTemplateElement.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 16e35be8-05e3-48aa-824f-8c3f7c331466

📥 Commits

Reviewing files that changed from the base of the PR and between 7a2be39 and cc98726.

⛔ Files ignored due to path filters (10)
  • 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_configuration/src/generated/linter_options_check.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/tests/specs/nursery/noReactStringRefs/invalid.jsx.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noReactStringRefs/react183/valid.jsx.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noReactStringRefs/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 (8)
  • .changeset/fifty-chicken-like.md
  • crates/biome_js_analyze/src/lint/nursery/no_react_string_refs.rs
  • crates/biome_js_analyze/tests/specs/nursery/noReactStringRefs/invalid.jsx
  • crates/biome_js_analyze/tests/specs/nursery/noReactStringRefs/react183/valid.jsx
  • crates/biome_js_analyze/tests/specs/nursery/noReactStringRefs/react183/valid.package.json
  • crates/biome_js_analyze/tests/specs/nursery/noReactStringRefs/valid.jsx
  • crates/biome_rule_options/src/lib.rs
  • crates/biome_rule_options/src/no_react_string_refs.rs
✅ Files skipped from review due to trivial changes (7)
  • crates/biome_js_analyze/tests/specs/nursery/noReactStringRefs/react183/valid.package.json
  • crates/biome_js_analyze/tests/specs/nursery/noReactStringRefs/react183/valid.jsx
  • .changeset/fifty-chicken-like.md
  • crates/biome_rule_options/src/lib.rs
  • crates/biome_js_analyze/tests/specs/nursery/noReactStringRefs/invalid.jsx
  • crates/biome_js_analyze/tests/specs/nursery/noReactStringRefs/valid.jsx
  • crates/biome_rule_options/src/no_react_string_refs.rs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-CLI Area: CLI A-Diagnostic Area: diagnostocis A-Linter Area: linter A-Project Area: project L-JavaScript Language: JavaScript and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants