Skip to content

feat(json_analyze): implement noTopLevelLiterals#9367

Merged
Netail merged 2 commits intobiomejs:mainfrom
Netail:feat/use-top-level-interop
Mar 6, 2026
Merged

feat(json_analyze): implement noTopLevelLiterals#9367
Netail merged 2 commits intobiomejs:mainfrom
Netail:feat/use-top-level-interop

Conversation

@Netail
Copy link
Copy Markdown
Member

@Netail Netail commented Mar 6, 2026

Summary

Implement Eslint JSON's top-level-interop, requiring the top-level value to be an array or object.

Unsure about the naming of the rule, open for new names (perhaps useTopLevelArrayOrObject ?)

Test Plan

unit tests

Docs

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 6, 2026

🦋 Changeset detected

Latest commit: 743eb27

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-JSON Language: JSON and super languages A-Diagnostic Area: diagnostocis labels Mar 6, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 6, 2026

Walkthrough

Adds a new nursery JSON lint rule noTopLevelLiterals that requires the document root to be an array or object. Introduces the rule implementation and metadata, a NoTopLevelLiteralsOptions options type, exposes the options module, and adds test fixtures covering valid roots (arrays, objects) and invalid roots (string, number, boolean, null). Also includes a changeset documenting a patch release for the @biomejs/biome package.

Suggested reviewers

  • ematipico
  • dyc3
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The description accurately covers the PR's intent to implement a top-level-interop style rule and mentions the naming uncertainty, which was resolved during review.
Title check ✅ Passed The title 'feat(json_analyze): implement noTopLevelLiterals' accurately summarises the main change—implementing a new JSON linting rule. It's concise, clear, and directly reflects the core work in 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
  • Post copyable unit tests in a comment

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.

🧹 Nitpick comments (1)
crates/biome_json_analyze/src/lint/nursery/use_top_level_interop.rs (1)

85-99: Consider using the value's range for more precise diagnostic highlighting.

Currently ctx.query().range() returns the range of the entire JsonRoot. For a more focused diagnostic, using the actual value's range would pinpoint the offending primitive rather than the whole document.

♻️ Proposed refinement
     fn diagnostic(ctx: &RuleContext<Self>, _state: &Self::State) -> Option<RuleDiagnostic> {
-        let span = ctx.query().range();
+        let node = ctx.query();
+        let value = node.value().ok()?;
+        let span = value.range();
         Some(
             RuleDiagnostic::new(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_json_analyze/src/lint/nursery/use_top_level_interop.rs` around
lines 85 - 99, The diagnostic currently highlights the whole JsonRoot by using
ctx.query().range(); change it to use the inner value's range for precise
highlighting (e.g., obtain the value via ctx.query().value() and use its range)
and fall back to ctx.query().range() if the value is missing; update the span
used in RuleDiagnostic::new inside the diagnostic function so the note and
diagnostic target the offending primitive rather than the entire document.
🤖 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_json_analyze/src/lint/nursery/use_top_level_interop.rs`:
- Around line 85-99: The diagnostic currently highlights the whole JsonRoot by
using ctx.query().range(); change it to use the inner value's range for precise
highlighting (e.g., obtain the value via ctx.query().value() and use its range)
and fall back to ctx.query().range() if the value is missing; update the span
used in RuleDiagnostic::new inside the diagnostic function so the note and
diagnostic target the offending primitive rather than the entire document.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b21a88b1-554c-4e81-a905-c3958463a0c4

📥 Commits

Reviewing files that changed from the base of the PR and between 59c086a and 596c68f.

⛔ Files ignored due to path filters (13)
  • 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_diagnostics_categories/src/categories.rs is excluded by !**/categories.rs and included by **
  • crates/biome_json_analyze/tests/specs/nursery/useTopLevelInterop/invalid/boolean.json.snap is excluded by !**/*.snap and included by **
  • crates/biome_json_analyze/tests/specs/nursery/useTopLevelInterop/invalid/null.json.snap is excluded by !**/*.snap and included by **
  • crates/biome_json_analyze/tests/specs/nursery/useTopLevelInterop/invalid/number.json.snap is excluded by !**/*.snap and included by **
  • crates/biome_json_analyze/tests/specs/nursery/useTopLevelInterop/invalid/string.json.snap is excluded by !**/*.snap and included by **
  • crates/biome_json_analyze/tests/specs/nursery/useTopLevelInterop/valid/array.json.snap is excluded by !**/*.snap and included by **
  • crates/biome_json_analyze/tests/specs/nursery/useTopLevelInterop/valid/empty-array.json.snap is excluded by !**/*.snap and included by **
  • crates/biome_json_analyze/tests/specs/nursery/useTopLevelInterop/valid/empty-object.json.snap is excluded by !**/*.snap and included by **
  • crates/biome_json_analyze/tests/specs/nursery/useTopLevelInterop/valid/object.json.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 (12)
  • .changeset/sour-beds-juggle.md
  • crates/biome_json_analyze/src/lint/nursery/use_top_level_interop.rs
  • crates/biome_json_analyze/tests/specs/nursery/useTopLevelInterop/invalid/boolean.json
  • crates/biome_json_analyze/tests/specs/nursery/useTopLevelInterop/invalid/null.json
  • crates/biome_json_analyze/tests/specs/nursery/useTopLevelInterop/invalid/number.json
  • crates/biome_json_analyze/tests/specs/nursery/useTopLevelInterop/invalid/string.json
  • crates/biome_json_analyze/tests/specs/nursery/useTopLevelInterop/valid/array.json
  • crates/biome_json_analyze/tests/specs/nursery/useTopLevelInterop/valid/empty-array.json
  • crates/biome_json_analyze/tests/specs/nursery/useTopLevelInterop/valid/empty-object.json
  • crates/biome_json_analyze/tests/specs/nursery/useTopLevelInterop/valid/object.json
  • crates/biome_rule_options/src/lib.rs
  • crates/biome_rule_options/src/use_top_level_interop.rs

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Mar 6, 2026

Merging this PR will not alter performance

✅ 15 untouched benchmarks
⏩ 196 skipped benchmarks1


Comparing Netail:feat/use-top-level-interop (743eb27) with main (59c086a)

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.

@ematipico
Copy link
Copy Markdown
Member

useTopLevelArrayOrObject

I like it!!

@ematipico
Copy link
Copy Markdown
Member

Actually, this rule should be a no*, because it disallows a concept strings/boolean/etc.

@Netail
Copy link
Copy Markdown
Member Author

Netail commented Mar 6, 2026

Actually, this rule should be a no*, because it disallows a concept strings/boolean/etc.

noTopLevelLiteral?

Comment on lines +85 to +99
fn diagnostic(ctx: &RuleContext<Self>, _state: &Self::State) -> Option<RuleDiagnostic> {
let span = ctx.query().range();
Some(
RuleDiagnostic::new(
rule_category!(),
span,
markup! {
"Expected the top-level value to be an array or object."
},
)
.note(markup! {
"To ensure maximal interoperability with the oldest JSON parsers, make sure the top-level value is an array or object."
}),
)
}
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.

the diagnostic could be improved. rule pillars.

@Netail Netail force-pushed the feat/use-top-level-interop branch from 596c68f to d00c1df Compare March 6, 2026 19:01
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: 2

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

Inline comments:
In @.changeset/sour-beds-juggle.md:
- Line 5: The release note line is a fragment; update the sentence about the new
rule `noTopLevelLiterals` to be a full, clear sentence—e.g., "Added the nursery
rule `noTopLevelLiterals`, which requires the root-level value to be an array or
object." Locate the line referencing `noTopLevelLiterals` and replace the
fragment with this polished wording.

In `@crates/biome_rule_options/src/no_top_level_literals.rs`:
- Line 6: Add inline rustdoc for the NoTopLevelLiteralsOptions struct: above the
declaration of NoTopLevelLiteralsOptions document what the options control
(e.g., toggle the rule behavior or note there are currently no configurable
fields), the intended usage, and any default behavior; use Rust doc comments
(///) so generated docs are complete and follow the guideline for rules/options
documentation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1f3fcc60-dc79-4cc2-a4a6-a31e20fc5abb

📥 Commits

Reviewing files that changed from the base of the PR and between 596c68f and d00c1df.

⛔ Files ignored due to path filters (13)
  • 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_diagnostics_categories/src/categories.rs is excluded by !**/categories.rs and included by **
  • crates/biome_json_analyze/tests/specs/nursery/noTopLevelLiterals/invalid/boolean.json.snap is excluded by !**/*.snap and included by **
  • crates/biome_json_analyze/tests/specs/nursery/noTopLevelLiterals/invalid/null.json.snap is excluded by !**/*.snap and included by **
  • crates/biome_json_analyze/tests/specs/nursery/noTopLevelLiterals/invalid/number.json.snap is excluded by !**/*.snap and included by **
  • crates/biome_json_analyze/tests/specs/nursery/noTopLevelLiterals/invalid/string.json.snap is excluded by !**/*.snap and included by **
  • crates/biome_json_analyze/tests/specs/nursery/noTopLevelLiterals/valid/array.json.snap is excluded by !**/*.snap and included by **
  • crates/biome_json_analyze/tests/specs/nursery/noTopLevelLiterals/valid/empty-array.json.snap is excluded by !**/*.snap and included by **
  • crates/biome_json_analyze/tests/specs/nursery/noTopLevelLiterals/valid/empty-object.json.snap is excluded by !**/*.snap and included by **
  • crates/biome_json_analyze/tests/specs/nursery/noTopLevelLiterals/valid/object.json.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 (12)
  • .changeset/sour-beds-juggle.md
  • crates/biome_json_analyze/src/lint/nursery/no_top_level_literals.rs
  • crates/biome_json_analyze/tests/specs/nursery/noTopLevelLiterals/invalid/boolean.json
  • crates/biome_json_analyze/tests/specs/nursery/noTopLevelLiterals/invalid/null.json
  • crates/biome_json_analyze/tests/specs/nursery/noTopLevelLiterals/invalid/number.json
  • crates/biome_json_analyze/tests/specs/nursery/noTopLevelLiterals/invalid/string.json
  • crates/biome_json_analyze/tests/specs/nursery/noTopLevelLiterals/valid/array.json
  • crates/biome_json_analyze/tests/specs/nursery/noTopLevelLiterals/valid/empty-array.json
  • crates/biome_json_analyze/tests/specs/nursery/noTopLevelLiterals/valid/empty-object.json
  • crates/biome_json_analyze/tests/specs/nursery/noTopLevelLiterals/valid/object.json
  • crates/biome_rule_options/src/lib.rs
  • crates/biome_rule_options/src/no_top_level_literals.rs
✅ Files skipped from review due to trivial changes (4)
  • crates/biome_json_analyze/tests/specs/nursery/noTopLevelLiterals/invalid/string.json
  • crates/biome_json_analyze/tests/specs/nursery/noTopLevelLiterals/valid/object.json
  • crates/biome_json_analyze/tests/specs/nursery/noTopLevelLiterals/valid/empty-array.json
  • crates/biome_json_analyze/tests/specs/nursery/noTopLevelLiterals/invalid/number.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/biome_rule_options/src/lib.rs

#[derive(Default, Clone, Debug, Deserialize, Deserializable, Merge, Eq, PartialEq, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields, default)]
pub struct NoTopLevelLiteralsOptions {}
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.

🛠️ Refactor suggestion | 🟠 Major

Add rustdoc for the options type.

NoTopLevelLiteralsOptions should have inline rustdoc so generated docs stay complete and consistent.

Suggested patch
+/// Options for the `noTopLevelLiterals` rule.
 pub struct NoTopLevelLiteralsOptions {}

As per coding guidelines, "**/*.rs: Use inline rustdoc documentation for rules, assists, and their options".

📝 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
pub struct NoTopLevelLiteralsOptions {}
/// Options for the `noTopLevelLiterals` rule.
pub struct NoTopLevelLiteralsOptions {}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_rule_options/src/no_top_level_literals.rs` at line 6, Add inline
rustdoc for the NoTopLevelLiteralsOptions struct: above the declaration of
NoTopLevelLiteralsOptions document what the options control (e.g., toggle the
rule behavior or note there are currently no configurable fields), the intended
usage, and any default behavior; use Rust doc comments (///) so generated docs
are complete and follow the guideline for rules/options documentation.

Copy link
Copy Markdown
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. Address the bot's feedback on the changeset

@Netail Netail changed the title feat(json_analyze): implement useTopLevelInterop feat(json_analyze): implement noTopLevelLiterals Mar 6, 2026
@Netail Netail merged commit 722f0da into biomejs:main Mar 6, 2026
21 checks passed
@Netail Netail deleted the feat/use-top-level-interop branch March 6, 2026 21:22
@github-actions github-actions bot mentioned this pull request Mar 6, 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-JSON Language: JSON and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants