Skip to content

feat(lint): add noDrizzleDeleteWithoutWhere and noDrizzleUpdateWithoutWhere rules#9525

Merged
dyc3 merged 12 commits intobiomejs:mainfrom
ViniciusDev26:feat/drizzle-lint-rules
Mar 18, 2026
Merged

feat(lint): add noDrizzleDeleteWithoutWhere and noDrizzleUpdateWithoutWhere rules#9525
dyc3 merged 12 commits intobiomejs:mainfrom
ViniciusDev26:feat/drizzle-lint-rules

Conversation

@ViniciusDev26
Copy link
Contributor

This PR was developed primarily with the assistance of Claude Code (Anthropic).

Summary

Closes #8114.

Adds two new nursery lint rules for Drizzle ORM, mirroring the rules from
eslint-plugin-drizzle:

  • noDrizzleDeleteWithoutWhere — disallows calling .delete() without a .where() clause, which would delete all
    rows in the table.
  • noDrizzleUpdateWithoutWhere — disallows calling .update().set() without a .where() clause, which would update
    all rows in the table.

Both rules are opt-in and require configuring the drizzleObjectName option with the variable names that represent
Drizzle ORM instances in the project. This is necessary because Drizzle does not enforce a specific variable name for the
database client.

{
  "linter": {
    "rules": {
      "nursery": {
        "noDrizzleDeleteWithoutWhere": {
          "level": "error",
          "options": {
            "drizzleObjectName": ["db"]
          }
        }
      }
    }
  }
}

The rules also support the drizzle domain, which will be automatically enabled when drizzle-orm is detected as a project
dependency:

{
  "linter": {
    "domains": {
      "drizzle": "all"
    }
  }
}

Infrastructure changes

  • Added RuleDomain::Drizzle to biome_analyze
  • Added RuleSource::EslintDrizzle to biome_analyze

Test Plan

Snapshot tests covering:

  • db.delete(table) without .where() → diagnostic
  • db.delete(table).where(...) → no diagnostic
  • db.update(table).set({...}) without .where() → diagnostic
  • db.update(table).set({...}).where(...) → no diagnostic
  • Variable not in drizzleObjectName (e.g. database.delete(...)) → no diagnostic

cargo test -p biome_js_analyze -- "specs::nursery::no_drizzle"

test result: ok. 8 passed

Docs

Documentation is inline in the rule source via rustdoc, including:

  • Rule description
  • Options section with JSON example
  • Examples with invalid and valid cases using use_options

ViniciusDev26 and others added 2 commits March 17, 2026 12:45
…outWhere rules

Add two new nursery lint rules for Drizzle ORM that prevent accidental
full-table deletes and updates by requiring a `.where()` clause.

Both rules are configurable via `drizzleObjectName` option, which specifies
the variable names that represent Drizzle ORM instances.

Also adds `RuleDomain::Drizzle` and `RuleSource::EslintDrizzle` to support
the new domain and link back to the original eslint-plugin-drizzle source.

This PR was developed with the assistance of Claude Code.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…noDrizzleUpdateWithoutWhere

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@changeset-bot
Copy link

changeset-bot bot commented Mar 17, 2026

🦋 Changeset detected

Latest commit: c1c1b8d

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-Linter Area: linter L-JavaScript Language: JavaScript and super languages A-Diagnostic Area: diagnostocis labels Mar 17, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 17, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds Drizzle support to the analyzer surface: a new RuleSource::EslintDrizzle(&str) and RuleDomain::Drizzle (slug "drizzle", manifest dependency drizzle-orm >= 0.29.0, empty globals). Wires the new source/domain into Display, variant indexing, rule-name resolution and URL generation. Introduces two ESLint-sourced rules (NoDrizzleDeleteWithoutWhere, NoDrizzleUpdateWithoutWhere), their options types and exports, framework helpers (get_identifier_name, has_where_in_chain), tests and fixtures, and registers the new rule option modules.

Possibly related PRs

  • #8898: Modifies the same RuleSource enum and Display/as_rule_name/variant_index/to_rule_url logic — directly overlaps the EslintDrizzle addition.
  • #8044: Adds and wires new ESLint rule source variants and updates rule.rs mappings — intersects with the Drizzle variant plumbing.
  • #8960: Introduces another ESLint plugin domain and updates rule/domain handling in rule.rs (Playwright) — touches the same helper branches and enum extensions.

Suggested labels

A-Project, A-Core

Suggested reviewers

  • dyc3
  • Netail
  • ematipico
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding two new Drizzle ORM lint rules.
Description check ✅ Passed The description is thorough and directly related to the changeset, detailing the new rules, configuration, infrastructure changes, and test coverage.
Linked Issues check ✅ Passed All coding requirements from issue #8114 are met: Drizzle domain added [rule.rs], ESLint Drizzle source added [rule.rs], both delete/update rules implemented [nursery/], and drizzleObjectName configuration supported [rule_options/].
Out of Scope Changes check ✅ Passed All changes directly support the stated objectives: new domain/source variants, two new lint rules with options, test fixtures, helper functions, and a changeset file. No unrelated modifications detected.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

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.

❤️ Share

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

Copy link
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: 4

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

118-157: Consider extracting shared helpers to avoid duplication.

get_identifier_name and has_where_in_chain are duplicated between this rule and no_drizzle_delete_without_where. Consider extracting them to a shared module (e.g., drizzle_utils.rs) to reduce maintenance burden.

🤖 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_drizzle_update_without_where.rs`
around lines 118 - 157, Extract the duplicated helpers get_identifier_name and
has_where_in_chain into a new shared module (e.g., create drizzle_utils.rs) that
exports both functions; replace the copies in this file
(no_drizzle_update_without_where.rs) and the other rule file
(no_drizzle_delete_without_where.rs) with imports from the new module (adjust
use/... paths accordingly), ensure the functions keep the same signatures and
any required visibility (pub(crate) or pub) and remove the duplicated
implementations so both rules call the shared helpers.
🤖 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_drizzle_delete_without_where.rs`:
- Around line 111-113: The inline backtick template syntax inside the
.note(markup! { ... }) may not render correctly; update the message in the
.note(markup! { ... }) call (in the no_drizzle_delete_without_where rule) to
either wrap code fragments with HTML <code> tags (e.g. <code>.where()</code> and
<code>.where(sql`1=1`)</code>) or replace backticks with HTML entities (&#96;)
so the `.where()` and the `sql`1=1`` fragment render reliably; adjust the string
passed to markup! accordingly.

In `@crates/biome_js_analyze/src/lint/nursery/no_drizzle_update_without_where.rs`:
- Around line 111-113: The markup! note string in the
no_drizzle_update_without_where rule contains nested backticks and a
template-literal-like fragment (`sql`1=1``) that may not render; update the
message constructed in .note(markup! { ... })—either escape or remove the inner
backticks and rephrase the example (e.g. use plain text like "use .where(sql
\"1=1\") to explicitly update all rows" or "use .where(sql(\"1=1\"))") so the
markup! macro renders correctly; locate the .note(markup! { ... }) call in the
no_drizzle_update_without_where.rs file to apply the change.

In
`@crates/biome_js_analyze/tests/specs/nursery/noDrizzleDeleteWithoutWhere/invalid/delete.js`:
- Around line 12-13: The test in invalid/delete.js is labeled as a negative case
("should NOT trigger") but resides in the invalid/ folder, which expects
diagnostics; move the case using database.delete(users) (and its accompanying
comment) out of the invalid/ directory into the valid/ specs (or create a
corresponding valid test file) so test suite semantics match the comment and
snapshot expectations; ensure the test filename and its snapshot are placed
under the valid/ folder so it won't be treated as an expected-diagnostic case.

In
`@crates/biome_js_analyze/tests/specs/nursery/noDrizzleUpdateWithoutWhere/invalid/update.js`:
- Around line 12-13: The test case currently asserts that "different drizzle
object name should NOT trigger" but is placed in the invalid spec set; move this
test file out of the invalid suite into the valid/specs area (or the equivalent
valid directory) so its semantics match its placement, ensuring the test that
calls database.update(users).set({ name: "John" }) is categorized with valid
tests and leaving any surrounding test metadata/assertions unchanged.

---

Nitpick comments:
In `@crates/biome_js_analyze/src/lint/nursery/no_drizzle_update_without_where.rs`:
- Around line 118-157: Extract the duplicated helpers get_identifier_name and
has_where_in_chain into a new shared module (e.g., create drizzle_utils.rs) that
exports both functions; replace the copies in this file
(no_drizzle_update_without_where.rs) and the other rule file
(no_drizzle_delete_without_where.rs) with imports from the new module (adjust
use/... paths accordingly), ensure the functions keep the same signatures and
any required visibility (pub(crate) or pub) and remove the duplicated
implementations so both rules call the shared helpers.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 318a23a4-814c-4f11-bb19-7ed678eade81

📥 Commits

Reviewing files that changed from the base of the PR and between bc709f6 and 58b6172.

⛔ Files ignored due to path filters (5)
  • crates/biome_diagnostics_categories/src/categories.rs is excluded by !**/categories.rs and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noDrizzleDeleteWithoutWhere/invalid/delete.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noDrizzleDeleteWithoutWhere/valid/delete.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noDrizzleUpdateWithoutWhere/invalid/update.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noDrizzleUpdateWithoutWhere/valid/update.js.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (14)
  • crates/biome_analyze/src/rule.rs
  • crates/biome_js_analyze/src/lint/nursery/no_drizzle_delete_without_where.rs
  • crates/biome_js_analyze/src/lint/nursery/no_drizzle_update_without_where.rs
  • crates/biome_js_analyze/tests/specs/nursery/noDrizzleDeleteWithoutWhere/invalid/delete.js
  • crates/biome_js_analyze/tests/specs/nursery/noDrizzleDeleteWithoutWhere/invalid/delete.options.json
  • crates/biome_js_analyze/tests/specs/nursery/noDrizzleDeleteWithoutWhere/valid/delete.js
  • crates/biome_js_analyze/tests/specs/nursery/noDrizzleDeleteWithoutWhere/valid/delete.options.json
  • crates/biome_js_analyze/tests/specs/nursery/noDrizzleUpdateWithoutWhere/invalid/update.js
  • crates/biome_js_analyze/tests/specs/nursery/noDrizzleUpdateWithoutWhere/invalid/update.options.json
  • crates/biome_js_analyze/tests/specs/nursery/noDrizzleUpdateWithoutWhere/valid/update.js
  • crates/biome_js_analyze/tests/specs/nursery/noDrizzleUpdateWithoutWhere/valid/update.options.json
  • crates/biome_rule_options/src/lib.rs
  • crates/biome_rule_options/src/no_drizzle_delete_without_where.rs
  • crates/biome_rule_options/src/no_drizzle_update_without_where.rs

@ViniciusDev26 ViniciusDev26 changed the title Feat/drizzle lint rules feat(lint): add noDrizzleDeleteWithoutWhere and noDrizzleUpdateWithoutWhere rules Mar 17, 2026
- Extract shared helpers get_identifier_name and has_where_in_chain to
  crates/biome_js_analyze/src/frameworks/drizzle.rs to avoid duplication
- Use <Emphasis> markup tags instead of raw backticks in diagnostic messages
- Move database.delete/update test cases from invalid/ to valid/ fixtures

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
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/frameworks/drizzle.rs`:
- Around line 18-27: The helper currently returns true for any
JsStaticMemberExpression with a member named "where" (using
JsStaticMemberExpression, member(), as_js_name(), value_token(),
token_text_trimmed()), but it must only match an actual call like `.where(...)`;
update the check to verify that the JsStaticMemberExpression is itself the
callee of a JsCallExpression (e.g., inspect the parent/ancestor and use
JsCallExpression::cast_ref to ensure the member expression is being invoked)
before returning true so bare member access like `.where;` does not count as a
where call.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 47cd33ba-7121-4e4a-9203-2f3f67ed47e7

📥 Commits

Reviewing files that changed from the base of the PR and between 644a953 and a547adb.

⛔ Files ignored due to path filters (2)
  • crates/biome_js_analyze/tests/specs/nursery/noDrizzleDeleteWithoutWhere/invalid/delete.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noDrizzleUpdateWithoutWhere/invalid/update.js.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (6)
  • crates/biome_js_analyze/src/frameworks/drizzle.rs
  • crates/biome_js_analyze/src/frameworks/mod.rs
  • crates/biome_js_analyze/src/lint/nursery/no_drizzle_delete_without_where.rs
  • crates/biome_js_analyze/src/lint/nursery/no_drizzle_update_without_where.rs
  • crates/biome_js_analyze/tests/specs/nursery/noDrizzleDeleteWithoutWhere/invalid/delete.js
  • crates/biome_js_analyze/tests/specs/nursery/noDrizzleUpdateWithoutWhere/invalid/update.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/biome_js_analyze/tests/specs/nursery/noDrizzleDeleteWithoutWhere/invalid/delete.js
  • crates/biome_js_analyze/src/lint/nursery/no_drizzle_delete_without_where.rs

Verify that the JsStaticMemberExpression with member "where" is the
callee of a JsCallExpression before returning true, so bare property
access like .where (without parentheses) does not incorrectly suppress
the diagnostic. Added test cases covering this scenario.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@codspeed-hq
Copy link

codspeed-hq bot commented Mar 17, 2026

Merging this PR will not alter performance

✅ 58 untouched benchmarks
⏩ 156 skipped benchmarks1


Comparing ViniciusDev26:feat/drizzle-lint-rules (c1c1b8d) with main (2f8bf80)2

Open in CodSpeed

Footnotes

  1. 156 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.

  2. No successful run was found on main (4b64145) during the generation of this report, so 2f8bf80 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@github-actions github-actions bot added A-CLI Area: CLI A-Project Area: project labels Mar 17, 2026
Copy link
Contributor

@dyc3 dyc3 left a comment

Choose a reason for hiding this comment

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

This will need a website PR to document the new domain.

- Link EslintDrizzle rule URLs to specific anchors on the docs page
- Lower drizzle-orm minimum version requirement from >=0.29.0 to >=0.9.0
- Split changeset into one per rule
- Mark both rules as recommended
- Change drizzleObjectName option type to Option<Box<[Box<str>]>> with manual Merge impl

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ViniciusDev26
Copy link
Contributor Author

Hey @dyc3, since both the rule pages and domains.mdx on the website are auto-generated from source, is a manual website PR still needed here?

@ViniciusDev26 ViniciusDev26 requested a review from dyc3 March 17, 2026 18:24
@dyc3
Copy link
Contributor

dyc3 commented Mar 17, 2026

Ah I had forgotten we automated that. Nevermind it then.

Copy link
Contributor

@dyc3 dyc3 left a comment

Choose a reason for hiding this comment

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

Looks good. Just need to fix the CI

@dyc3 dyc3 merged commit e7b3b10 into biomejs:main Mar 18, 2026
31 checks passed
@github-actions github-actions bot mentioned this pull request Mar 18, 2026
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.

📎 Create domain for drizzle

2 participants