Deprecated: Function get_magic_quotes_gpc() is deprecated in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 99

Deprecated: The each() function is deprecated. This message will be suppressed on further calls in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 619

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1169

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176
8000 feat(useSortedKeys): add groupByNesting option by PaulRBerg · Pull Request #7799 · biomejs/biome · GitHub
Nothing Special   »   [go: up one dir, main page]

Skip to content
Open
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
37 changes: 37 additions & 0 deletions .changeset/group-by-nesting-feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
"@biomejs/biome": minor
---

Added `groupByNesting` option to the `useSortedKeys` assist. When enabled, object keys are grouped by their value's nesting depth before sorting alphabetically.

Simple values (primitives and single-line arrays) are sorted first, followed by nested values (objects and multi-line arrays).

#### Example

```json
{
"options": {
"groupByNesting": true
}
}
```

With this option, the following unsorted object:

```js
{
"name": "Sample",
"details": { "description": "nested" },
"id": "123"
}
```

Will be sorted as:

```js
{
"id": "123",
"name": "Sample",
"details": { "description": "nested" }
}
```
120 changes: 104 additions & 16 deletions crates/biome_js_analyze/src/assist/source/use_sorted_keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ use biome_console::markup;
use biome_deserialize::TextRange;
use biome_diagnostics::{Applicability, category};
use biome_js_factory::make;
use biome_js_syntax::{JsObjectExpression, JsObjectMemberList, T};
use biome_js_syntax::{
AnyJsExpression, AnyJsObjectMember, JsObjectExpression, JsObjectMemberList, T,
};
use biome_rowan::{AstNode, BatchMutationExt, TriviaPieceKind};
use biome_rule_options::use_sorted_keys::{SortOrder, UseSortedKeysOptions};
use biome_string_case::comparable_token::ComparableToken;
Expand Down Expand Up @@ -129,6 +131,26 @@ declare_source_rule! {
/// };
/// ```
///
/// ### `groupByNesting`
/// When enabled, groups object keys by their value's nesting depth before sorting alphabetically.
///
/// ```json,options
/// {
/// "options": {
/// "groupByNesting": true
/// }
/// }
/// ```
/// ```js,use_options,expect_diagnostic
/// const obj = {
/// name: "Sample",
/// details: {
/// description: "nested"
/// },
/// id: "123"
/// };
/// ```
///
pub UseSortedKeys {
version: "2.0.0",
name: "useSortedKeys",
Expand All @@ -139,6 +161,43 @@ declare_source_rule! {
}
}

/// Determines the nesting depth of a JavaScript expression for grouping purposes.
fn get_nesting_depth_js(value: &AnyJsExpression) -> u8 {
match value {
AnyJsExpression::JsObjectExpression(_) => 1,
AnyJsExpression::JsArrayExpression(array) => {
// Check if array spans multiple lines by looking for newlines
if array.syntax().text_trimmed().contains_char('\n') {
1
} else {
0
}
}
// Function and class expressions are treated as nested
AnyJsExpression::JsArrowFunctionExpression(_)
| AnyJsExpression::JsFunctionExpression(_)
| AnyJsExpression::JsClassExpression(_) => 1,
_ => 0,
}
}

/// Determines the nesting depth for an object member:
/// - properties: based on value expression;
/// - methods/getters/setters: treat as nested (1);
/// - spreads/computed or unnamed: non-sortable (None).
fn get_member_depth(node: &AnyJsObjectMember) -> Option<u8> {
match node {
AnyJsObjectMember::JsPropertyObjectMember(prop) => {
let value = prop.value().ok()?;
Some(get_nesting_depth_js(&value))
}
AnyJsObjectMember::JsMethodObjectMember(_)
| AnyJsObjectMember::JsGetterObjectMember(_)
| AnyJsObjectMember::JsSetterObjectMember(_) => Some(1),
_ => None,
}
}

impl Rule for UseSortedKeys {
type Query = Ast<JsObjectMemberList>;
type State = ();
Expand All @@ -153,14 +212,29 @@ impl Rule for UseSortedKeys {
SortOrder::Lexicographic => ComparableToken::lexicographic_cmp,
};

is_separated_list_sorted_by(
ctx.query(),
|node| node.name().map(ComparableToken::new),
comparator,
)
.ok()?
.not()
.then_some(())
if options.group_by_nesting {
is_separated_list_sorted_by(
ctx.query(),
|node| {
let depth = get_member_depth(node)?;
let name = node.name().map(ComparableToken::new)?;
Some((depth, name))
},
|(d1, n1), (d2, n2)| d1.cmp(d2).then_with(|| comparator(n1, n2)),
)
.ok()?
.not()
.then_some(())
} else {
is_separated_list_sorted_by(
ctx.query(),
|node| node.name().map(ComparableToken::new),
comparator,
)
.ok()?
.not()
.then_some(())
}
}

fn diagnostic(ctx: &RuleContext<Self>, _state: &Self::State) -> Option<RuleDiagnostic> {
Expand Down Expand Up @@ -190,13 +264,27 @@ impl Rule for UseSortedKeys {
SortOrder::Lexicographic => ComparableToken::lexicographic_cmp,
};

let new_list = sorted_separated_list_by(
list,
|node| node.name().map(ComparableToken::new),
|| make::token(T![,]).with_trailing_trivia([(TriviaPieceKind::Whitespace, " ")]),
comparator,
)
.ok()?;
let new_list = if options.group_by_nesting {
sorted_separated_list_by(
list,
|node| {
let depth = get_member_depth(node)?;
let name = node.name().map(ComparableToken::new)?;
Some((depth, name))
},
|| make::token(T![,]).with_trailing_trivia([(TriviaPieceKind::Whitespace, " ")]),
|(d1, n1), (d2, n2)| d1.cmp(d2).then_with(|| comparator(n1, n2)),
)
.ok()?
} else {
sorted_separated_list_by(
list,
|node| node.name().map(ComparableToken::new),
|| make::token(T![,]).with_trailing_trivia([(TriviaPieceKind::Whitespace, " ")]),
comparator,
)
.ok()?
};

let mut mutation = ctx.root().begin();
mutation.replace_node_discard_trivia(list.clone(), new_list);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const obj = {
name: "Sample Item",
details: {
description: "This is a nested object",
status: "active"
},
id: "12345",
tags: ["short", "array"],
metadata: {
created: "2024-01-01",
updated: "2024-01-02"
},
count: 42,
multiLineArray: [
"item1",
"item2",
"item3"
]
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
assertion_line: 152
expression: group-by-nesting-lexicographic.js
---
# Input
```js
const obj = {
name: "Sample Item",
details: {
description: "This is a nested object",
status: "active"
},
id: "12345",
tags: ["short", "array"],
metadata: {
created: "2024-01-01",
updated: "2024-01-02"
},
count: 42,
multiLineArray: [
"item1",
"item2",
"item3"
]
};

```

# Diagnostics
```
group-by-nesting-lexicographic.js:2:3 assist/source/useSortedKeys FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━

i The object properties are not sorted by key.

1 │ const obj = {
> 2 │ name: "Sample Item",
│ ^^^^^^^^^^^^^^^^^^^^
> 3 │ details: {
> 4 │ description: "This is a nested object",
...
> 17 │ "item3"
> 18 │ ]
│ ^
19 │ };
20 │

i Safe fix: Sort the object properties by key.

1 1 │ const obj = {
2 │ - ··name:·"Sample·Item",
3 │ - ··details:·{
2 │ + ··count:·42,
3 │ + ··id:·"12345",
4 │ + ··name:·"Sample·Item",
5 │ + ··tags:·["short",·"array"],
6 │ + ··details:·{
4 7 │ description: "This is a nested object",
5 8 │ status: "active"
6 9 │ },
7 │ - ··id:·"12345",
8 │ - ··tags:·["short",·"array"],
9 │ - ··metadata:·{
10 │ + ··metadata:·{
10 11 │ created: "2024-01-01",
11 12 │ updated: "2024-01-02"
12 │ - ··},
13 │ - ··count:·42,
13 │ + ··},
14 14 │ multiLineArray: [
15 15 │ "item1",


```
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "../../../../../../packages/@biomejs/biome/configuration_schema.json",
"assist": {
"actions": {
"source": {
"useSortedKeys": {
"level": "on",
"options": {
"sortOrder": "lexicographic",
"groupByNesting": true
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const obj = {
name: "Sample Item",
details: {
description: "This is a nested object",
status: "active"
},
id: "12345",
tags: ["short", "array"],
metadata: {
created: "2024-01-01",
updated: "2024-01-02"
},
count: 42,
multiLineArray: [
"item1",
"item2",
"item3"
]
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
assertion_line: 152
expression: group-by-nesting-natural.js
---
# Input
```js
const obj = {
name: "Sample Item",
details: {
description: "This is a nested object",
status: "active"
},
id: "12345",
tags: ["short", "array"],
metadata: {
created: "2024-01-01",
updated: "2024-01-02"
},
count: 42,
multiLineArray: [
"item1",
"item2",
"item3"
]
};

```

# Diagnostics
```
group-by-nesting-natural.js:2:3 assist/source/useSortedKeys FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

i The object properties are not sorted by key.

1 │ const obj = {
> 2 │ name: "Sample Item",
│ ^^^^^^^^^^^^^^^^^^^^
> 3 │ details: {
> 4 │ description: "This is a nested object",
...
> 17 │ "item3"
> 18 │ ]
│ ^
19 │ };
20 │

i Safe fix: Sort the object properties by key.

1 1 │ const obj = {
2 │ - ··name:·"Sample·Item",
3 │ - ··details:·{
2 │ + ··count:·42,
3 │ + ··id:·"12345",
4 │ + ··name:·"Sample·Item",
5 │ + ··tags:·["short",·"array"],
6 │ + ··details:·{
4 7 │ description: "This is a nested object",
5 8 │ status: "active"
6 9 │ },
7 │ - ··id:·"12345",
8 │ - ··tags:·["short",·"array"],
9 │ - ··metadata:·{
10 │ + ··metadata:·{
10 11 │ created: "2024-01-01",
11 12 │ updated: "2024-01-02"
12 │ - ··},
13 │ - ··count:·42,
13 │ + ··},
14 14 │ multiLineArray: [
15 15 │ "item1",


```
Loading
Loading
0