Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/sour-beds-juggle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@biomejs/biome": patch
---

Added the nursery rule [`noTopLevelLiterals`](https://biomejs.dev/linter/rules/no-top-level-literals/). It requires the root-level value to be an array or object.

**Invalid:**

```json
"just a string"
```
12 changes: 12 additions & 0 deletions crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs

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

4 changes: 4 additions & 0 deletions crates/biome_configuration/src/analyzer/linter/rules.rs

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

5 changes: 3 additions & 2 deletions crates/biome_diagnostics_categories/src/categories.rs

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

100 changes: 100 additions & 0 deletions crates/biome_json_analyze/src/lint/nursery/no_top_level_literals.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use biome_analyze::{
Ast, Rule, RuleDiagnostic, RuleSource, context::RuleContext, declare_lint_rule,
};
use biome_console::markup;
use biome_json_syntax::{AnyJsonValue, JsonRoot};
use biome_rowan::AstNode;
use biome_rule_options::no_top_level_literals::NoTopLevelLiteralsOptions;

declare_lint_rule! {
/// Require the JSON top-level value to be an array or object.
///
/// The JSON specification technically allows any JSON value (object, array, string, number, boolean, or null) to be used as the top-level element of a JSON document.
/// However, some older JSON parsers, especially those created before [RFC 7158](https://datatracker.ietf.org/doc/html/rfc7158)/[4627](https://datatracker.ietf.org/doc/html/rfc4627) was fully adopted, only support objects or arrays as the root element.
///
/// Additionally, some security practices (such as those preventing JSON hijacking attacks) rely on the assumption that the top-level value is an object or array.
/// Using an object or array at the top level also provides better extensibility for your data structures over time.
///
/// ## Examples
///
/// ### Invalid
///
/// ```json,expect_diagnostic
/// "just a string"
/// ```
///
/// ```json,expect_diagnostic
/// 42
/// ```
///
/// ```json,expect_diagnostic
/// true
/// ```
///
/// ```json,expect_diagnostic
/// null
/// ```
///
/// ### Valid
///
/// ```json
/// {
/// "property": "value",
/// "otherProperty": 123
/// }
/// ```
///
/// ```json
/// ["element", "anotherElement"]
/// ```
///
/// ```json
/// {}
/// ```
///
/// ```json
/// []
/// ```
///
pub NoTopLevelLiterals {
version: "next",
name: "noTopLevelLiterals",
language: "json",
recommended: false,
sources: &[RuleSource::EslintJson("top-level-interop").same()],
}
}

impl Rule for NoTopLevelLiterals {
type Query = Ast<JsonRoot>;
type State = ();
type Signals = Option<Self::State>;
type Options = NoTopLevelLiteralsOptions;

fn run(ctx: &RuleContext<Self>) -> Self::Signals {
let node = ctx.query();
let value = node.value().ok()?;

match value {
AnyJsonValue::JsonObjectValue(_) => None,
AnyJsonValue::JsonArrayValue(_) => None,
_ => Some(()),
}
}

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! {
"Some JSON parsers only support objects or arrays as the root element. Wrap your value in an array or object to ensure compatibility."
}),
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
true
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
source: crates/biome_json_analyze/tests/spec_tests.rs
expression: boolean.json
---
# Input
```json
true

```

# Diagnostics
```
boolean.json:1:1 lint/nursery/noTopLevelLiterals ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

i Expected the top-level value to be an array or object.

> 1 │ true
│ ^^^^
2 │

i Some JSON parsers only support objects or arrays as the root element. Wrap your value in an array or object to ensure compatibility.

i This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.


```
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
null
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
source: crates/biome_json_analyze/tests/spec_tests.rs
expression: null.json
---
# Input
```json
null
```

# Diagnostics
```
null.json:1:1 lint/nursery/noTopLevelLiterals ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
i Expected the top-level value to be an array or object.
> 1 │ null
│ ^^^^
2 │
i Some JSON parsers only support objects or arrays as the root element. Wrap your value in an array or object to ensure compatibility.
i This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.
```
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
42
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
source: crates/biome_json_analyze/tests/spec_tests.rs
expression: number.json
---
# Input
```json
42
```

# Diagnostics
```
number.json:1:1 lint/nursery/noTopLevelLiterals ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
i Expected the top-level value to be an array or object.
> 1 │ 42
│ ^^
2 │
i Some JSON parsers only support objects or arrays as the root element. Wrap your value in an array or object to ensure compatibility.
i This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.
```
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"just a string"
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
source: crates/biome_json_analyze/tests/spec_tests.rs
expression: string.json
---
# Input
```json
"just a string"

```

# Diagnostics
```
string.json:1:1 lint/nursery/noTopLevelLiterals ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

i Expected the top-level value to be an array or object.

> 1 │ "just a string"
│ ^^^^^^^^^^^^^^^
2 │

i Some JSON parsers only support objects or arrays as the root element. Wrap your value in an array or object to ensure compatibility.

i This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.


```
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[
"one",
"two",
"three"
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
source: crates/biome_json_analyze/tests/spec_tests.rs
expression: array.json
---
# Input
```json
[
"one",
"two",
"three"
]
```
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
source: crates/biome_json_analyze/tests/spec_tests.rs
expression: empty-array.json
---
# Input
```json
[]
```
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
source: crates/biome_json_analyze/tests/spec_tests.rs
expression: empty-object.json
---
# Input
```json
{}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"one": "value",
"two": "value",
"three": "value"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
source: crates/biome_json_analyze/tests/spec_tests.rs
expression: object.json
---
# Input
```json
{
"one": "value",
"two": "value",
"three": "value"
}

```
1 change: 1 addition & 0 deletions crates/biome_rule_options/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ pub mod no_template_curly_in_string;
pub mod no_ternary;
pub mod no_then_property;
pub mod no_this_in_static;
pub mod no_top_level_literals;
pub mod no_ts_ignore;
pub mod no_unassigned_variables;
pub mod no_undeclared_dependencies;
Expand Down
6 changes: 6 additions & 0 deletions crates/biome_rule_options/src/no_top_level_literals.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
use biome_deserialize_macros::{Deserializable, Merge};
use serde::{Deserialize, Serialize};
#[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.

Loading