Conversation
🦋 Changeset detectedLatest commit: d421fd8 The changes in this PR will be included in the next version bump. This PR includes changesets to release 14 packages
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 |
Merging this PR will not alter performance
Comparing Footnotes
|
Co-authored-by: Arthur Fiorette <me@arthur.place>
Co-authored-by: Arthur Fiorette <me@arthur.place>
Co-authored-by: Arthur Fiorette <me@arthur.place>
WalkthroughAdds an organizePackageJson assist action (exposed as UseSortedPackageJson) and wires it as an assist-action. Introduces RuleSource::SortPackageJson and a data-driven package.json field schema plus multiple sorter modules (constants, helpers, dependencies, dependencies_meta, scripts, eslint_config, prettier_config, exports) under a unified try_transform_field entry point. Implements JSON object/array sorting/transform utilities, test fixtures verifying compatibility with sort-package-json, a UseSortedPackageJsonOptions type, small JSON syntax API adjustments, and several simplifications to inner_string_text/value_token unwrap handling across analyzers and migration tooling. Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (1)
crates/biome_json_analyze/src/lint/nursery/use_required_scripts.rs (1)
106-115: Consider avoiding intermediateVec<String>allocation.Per repository guidance on avoiding string allocations for comparisons, you could skip collecting into
existing_scriptsand compare directly usingTokenText:♻️ Suggested refactor
- let existing_scripts: Vec<String> = scripts_object - .json_member_list() - .iter() - .flatten() - .filter_map(|member| { - let name = member.name().ok()?; - let text = name.inner_string_text()?; - Some(text.text().to_string()) - }) - .collect(); - - let missing_scripts: Vec<String> = options + let members = scripts_object.json_member_list(); + let missing_scripts: Vec<String> = options .required_scripts .iter() - .filter(|script| !existing_scripts.iter().any(|s| s == *script)) + .filter(|script| { + !members.iter().flatten().any(|member| { + member + .name() + .ok() + .and_then(|n| n.inner_string_text()) + .is_some_and(|text| text.text() == script.as_str()) + }) + }) .cloned() .collect();Based on learnings: "Avoid string allocations by using
&strorTokenTextfor comparisons instead of callingto_string()".🤖 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_required_scripts.rs` around lines 106 - 115, The code currently builds an intermediate Vec<String> called existing_scripts by calling to_string() on each member name; instead avoid the allocation by operating on TokenText/&str directly — e.g., use scripts_object.json_member_list().iter().flatten().filter_map(|member| member.name().ok()?.inner_string_text().map(|t| t.text())).then either collect into a HashSet<&TokenText> or use the iterator with any/contains against the TokenText/&str you need to compare; update places referencing existing_scripts to use the new iterator/collection so no to_string() allocations occur (look for symbols scripts_object, json_member_list, existing_scripts, and name()/inner_string_text()).
🤖 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_analyze/src/rule.rs`:
- Line 460: The URL mapped for Self::SortPackageJson is wrong (it's not an npm
CLI command); update the mapping in rule.rs so SortPackageJson points to the
third‑party package page (for example the npm package URL
"https://www.npmjs.com/package/sort-package-json" or the project repo
"https://github.com/keithamus/sort-package-json") by replacing the existing
string for Self::SortPackageJson with the correct package URL.
In `@crates/biome_json_analyze/src/assist/source/use_sorted_package_json.rs`:
- Around line 370-405: The current organize_members function uses a
HashMap<TokenText, JsonMember> (member_map) which collapses duplicate keys and
loses members; instead collect a Vec of (TokenText, JsonMember) from the
incoming JsonMemberList (preserving duplicates and original occurrence order),
then build a grouping (e.g., HashMap<TokenText, Vec<JsonMember>> or better an
ordered map like Vec<(TokenText, Vec<JsonMember>)>) so each key maps to a queue
of all members; iterate sorted_names from get_sorted_field_order and for each
name drain/iterate its Vec of JsonMember values, applying
apply_field_transformer to each member and pushing them onto elements (adding
separators between entries), and return make::json_member_list(elements,
separators); do not replace member_map with a single-value map that drops
duplicates and ensure JsonMemberList ordering for duplicate keys is preserved.
In
`@crates/biome_json_analyze/src/assist/source/use_sorted_package_json/sorters/dependencies_meta.rs`:
- Around line 37-81: The transform function currently bails out when
sort_object_by_comparator returns None so nested objects never get deep‑sorted;
update transform to always call deep_sort_nested_objects on the object (use the
sorted object when sort_object_by_comparator returns Some or the original object
when it returns None) and only return Some(AnyJsonValue::from(...)) if either
the top-level sort or the deep sort produced changes. Also change
deep_sort_nested_objects to return None when it detects no changes (instead of
Some(object.clone())), using super::helpers::sort_alphabetically_deep to detect
nested changes; reference functions: transform, sort_object_by_comparator,
deep_sort_nested_objects, get_package_name, and
super::helpers::sort_alphabetically_deep.
In
`@crates/biome_json_analyze/src/assist/source/use_sorted_package_json/sorters/prettier_config.rs`:
- Around line 5-68: Summary: transform currently sorts top-level keys but leaves
the "overrides" array and each override's "options" object unsorted; update it
to alphabetise both. Fix: in transform (function transform, variables
overrides_member, keys_with_members, make::json_member_list) when
overrides_member is Some, parse its JsonMember value as a JSON array, iterate
each array element that is an object and rebuild each override object by sorting
its members alphabetically and, if it has an "options" member, parse that
options value as an object and sort its members alphabetically too; then
reassemble a new overrides JsonMember with a json array of sorted override
objects and use that sorted overrides member when building new_members so
overrides remains last; add a unit test that supplies a prettier config with
unsorted overrides and options and asserts alphabetical ordering.
In `@crates/biome_json_analyze/tests/compat_sortpkg_tests.rs`:
- Around line 210-277: The test function test_compat_with_sortpkg currently
prints a success message with eprintln!("All {total} sort-package-json
compatibility tests passed"); replace that debug output by using dbg!(...) or
remove it per guideline: change the eprintln! call to a dbg!(format!("All {}
sort-package-json compatibility tests passed", total)) (or simply drop the
success print) so the test uses the dbg! macro for debugging output instead of
eprintln!.
---
Nitpick comments:
In `@crates/biome_json_analyze/src/lint/nursery/use_required_scripts.rs`:
- Around line 106-115: The code currently builds an intermediate Vec<String>
called existing_scripts by calling to_string() on each member name; instead
avoid the allocation by operating on TokenText/&str directly — e.g., use
scripts_object.json_member_list().iter().flatten().filter_map(|member|
member.name().ok()?.inner_string_text().map(|t| t.text())).then either collect
into a HashSet<&TokenText> or use the iterator with any/contains against the
TokenText/&str you need to compare; update places referencing existing_scripts
to use the new iterator/collection so no to_string() allocations occur (look for
symbols scripts_object, json_member_list, existing_scripts, and
name()/inner_string_text()).
...es/biome_json_analyze/src/assist/source/use_sorted_package_json/sorters/dependencies_meta.rs
Show resolved
Hide resolved
crates/biome_json_analyze/src/assist/source/use_sorted_package_json/sorters/prettier_config.rs
Show resolved
Hide resolved
a507e67 to
ad46c7b
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (3)
crates/biome_json_analyze/src/assist/source/use_sorted_package_json/sorters/helpers.rs (1)
237-264: Consider usingrustc_hash::FxHashSetfor consistency.The crate already depends on
rustc-hashper the library context. UsingFxHashSetinstead ofstd::collections::HashSetwould be more consistent and potentially faster for string keys.Suggested change
+use rustc_hash::FxHashSet; + /// Remove duplicate string values from an array pub fn uniq_array(array: &AnyJsonValue) -> Option<AnyJsonValue> { let array_value = array.as_json_array_value()?; let elements = array_value.elements(); - let mut seen: std::collections::HashSet<TokenText> = std::collections::HashSet::new(); + let mut seen: FxHashSet<TokenText> = FxHashSet::default();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/biome_json_analyze/src/assist/source/use_sorted_package_json/sorters/helpers.rs` around lines 237 - 264, The function uniq_array uses std::collections::HashSet for seen; replace it with rustc_hash::FxHashSet to match the crate's dependency and improve performance: import rustc_hash::FxHashSet (or prefix it as rustc_hash::FxHashSet) and change the variable declaration from std::collections::HashSet<TokenText> to FxHashSet<TokenText>, and construct it with FxHashSet::default() or FxHashSet::with_capacity(...) instead of HashSet::new(); keep all other logic in uniq_array (and references to TokenText) unchanged.crates/biome_json_analyze/tests/compat_sortpkg/data.json (2)
40-42: Duplicate test names may cause confusion.Lines 22 and 41 both use
"Should sort \dependenciesMeta` as object."Similarly, multipleexports` tests share identical names (lines 227, 246, 265, etc.). Consider adding distinguishing suffixes (e.g., "with scoped packages", "with versioned keys") to aid debugging when tests fail.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/biome_json_analyze/tests/compat_sortpkg/data.json` around lines 40 - 42, The JSON test entries in data.json have duplicate "testName" values (e.g., multiple "Should sort `dependenciesMeta` as object." and repeated "exports" test names), which makes failing tests ambiguous; update each offending test object by making its "testName" unique—append a short distinguishing suffix such as "—with scoped packages", "—with versioned keys", or "—case X" to the "testName" field for the entries that test different scenarios so each test can be identified unambiguously (locate and edit the "testName" properties in the JSON objects that start with "Should sort `dependenciesMeta` as object." and the repeated "exports" tests).
183-184: Clarify the"input": nullpattern.Several test cases use
"input": null(lines 184, 391, 415, 687, 711). If this means "output serves as both input and expected result", consider adding a comment at the top of the file or in a README explaining this convention for future maintainers.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/biome_json_analyze/tests/compat_sortpkg/data.json` around lines 183 - 184, Several test entries in data.json use the pattern "input": null to indicate that the test's output value should be used as the input (i.e., expected result doubles as input); add a short clarifying note at the top of this JSON test data file (or in an adjacent README) that documents this convention and lists the meaning of "input": null, referencing the "testName" fields (for example entries like "Should sort `eslintConfig.override[]` same as `eslintConfig`") and the "input"/"output" keys so future maintainers understand that null input means "use output as input."
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@crates/biome_analyze/src/rule.rs`:
- Line 460: The URL for Self::SortPackageJson is incorrect (points to a
non-existent npm CLI command); update the string returned for the
SortPackageJson variant to the official package page (for example
"https://www.npmjs.com/package/sort-package-json" or the GitHub repo
"https://github.com/keithamus/sort-package-json") in the match/impl handling the
rule URLs so the variant Self::SortPackageJson returns the correct third-party
package URL.
In `@crates/biome_json_analyze/src/assist/source/use_sorted_package_json.rs`:
- Around line 370-405: The current organize_members collects members into a
HashMap<TokenText, JsonMember> which drops duplicate keys; change this to
collect into a HashMap<TokenText, Vec<JsonMember>> (or similar per-key queue) so
all JsonMember occurrences are preserved in insertion order, then when iterating
sorted_names fetch the Vec for that key and push each member (applying
apply_field_transformer to each) into elements in the original occurrence order,
adding separators between members as before and finally build the JsonMemberList
via make::json_member_list; keep using extract_field_names and
get_sorted_field_order for the ordering but replace the single-value member_map
with a per-key list to avoid collapsing duplicates.
In
`@crates/biome_json_analyze/src/assist/source/use_sorted_package_json/sorters/dependencies_meta.rs`:
- Around line 37-81: transform currently short‑circuits when
sort_object_by_comparator returns None and deep_sort_nested_objects always
returns Some even if unchanged; change transform to always obtain a
JsonObjectValue to pass into deep_sort_nested_objects (use the sorted_top_level
if Some or the original object.clone() if None) so nested objects are inspected
even when top-level ordering didn’t change, and modify deep_sort_nested_objects
to return None when no nested changes were made (only return
Some(updated_object) when any member was replaced with a deep-sorted value);
reference functions: transform, sort_object_by_comparator,
deep_sort_nested_objects, and super::helpers::sort_alphabetically_deep.
In `@crates/biome_json_analyze/tests/compat_sortpkg_tests.rs`:
- Around line 210-277: In test_compat_with_sortpkg replace the debug eprintln!
call that prints the success message with either a dbg! invocation or remove it:
locate the eprintln!("All {total} sort-package-json compatibility tests passed")
in the test_compat_with_sortpkg function and change it to dbg!(format!("All {}
sort-package-json compatibility tests passed", total)) (or simply delete the
line) so debug output uses dbg! or is dropped per guidelines.
---
Nitpick comments:
In
`@crates/biome_json_analyze/src/assist/source/use_sorted_package_json/sorters/helpers.rs`:
- Around line 237-264: The function uniq_array uses std::collections::HashSet
for seen; replace it with rustc_hash::FxHashSet to match the crate's dependency
and improve performance: import rustc_hash::FxHashSet (or prefix it as
rustc_hash::FxHashSet) and change the variable declaration from
std::collections::HashSet<TokenText> to FxHashSet<TokenText>, and construct it
with FxHashSet::default() or FxHashSet::with_capacity(...) instead of
HashSet::new(); keep all other logic in uniq_array (and references to TokenText)
unchanged.
In `@crates/biome_json_analyze/tests/compat_sortpkg/data.json`:
- Around line 40-42: The JSON test entries in data.json have duplicate
"testName" values (e.g., multiple "Should sort `dependenciesMeta` as object."
and repeated "exports" test names), which makes failing tests ambiguous; update
each offending test object by making its "testName" unique—append a short
distinguishing suffix such as "—with scoped packages", "—with versioned keys",
or "—case X" to the "testName" field for the entries that test different
scenarios so each test can be identified unambiguously (locate and edit the
"testName" properties in the JSON objects that start with "Should sort
`dependenciesMeta` as object." and the repeated "exports" tests).
- Around line 183-184: Several test entries in data.json use the pattern
"input": null to indicate that the test's output value should be used as the
input (i.e., expected result doubles as input); add a short clarifying note at
the top of this JSON test data file (or in an adjacent README) that documents
this convention and lists the meaning of "input": null, referencing the
"testName" fields (for example entries like "Should sort
`eslintConfig.override[]` same as `eslintConfig`") and the "input"/"output" keys
so future maintainers understand that null input means "use output as input."
|
Sorry for not having time to fix all the past reviews :/ Work is demanding a lot for the last weeks... Thanks for picking this up, great work!! |
|
|
||
| fn run(ctx: &RuleContext<Self>) -> Option<Self::State> { | ||
| // Skip package.json files - they have specialized sorting via organizePackageJson | ||
| let path = ctx.file_path(); |
There was a problem hiding this comment.
This is one thing I was not sure when I did it... Maybe we should only disable this when useSortedPackageJson is enabled?
There was a problem hiding this comment.
It's actually a breaking change, so I will revert this part. We can enhance this diagnostic, and tell the user to use the new action if the file is package.json
There was a problem hiding this comment.
I agree as long as useSortedPackageJson wins when both are enabled
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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_analyze/src/rule.rs`:
- Around line 294-297: The variant_index mapping currently assigns the same
index 38 to Self::HtmlEslint and Self::EslintE18e, causing duplicate indices;
update the mapping so each enum variant has a unique sequential index by
changing Self::EslintE18e to return 39 (and increment subsequent indices
accordingly—e.g., change Self::EslintBetterTailwindcss to 40 and
Self::SortPackageJson to 41) in the function/match that implements variant_index
to restore deterministic ordering.
In `@crates/biome_json_analyze/src/assist/source/use_sorted_keys.rs`:
- Around line 214-216: The comment incorrectly states we "Skip package.json
files - they have specialized sorting via organizePackageJson" while the code
only retrieves the path (ctx.file_path()) and proceeds with the
useSortedPackageJson action; either implement an actual skip or update the
comment to match behavior — I recommend updating the comment to something like
"Handle package.json with useSortedPackageJson note" or, if you want to skip,
add a guard that checks the filename (e.g., if ctx.file_path().file_name() ==
Some("package.json")) and branch to organizePackageJson or return early;
reference use_sorted_keys.rs, ctx.file_path(), organizePackageJson and
useSortedPackageJson when making the change.
In
`@crates/biome_json_analyze/src/assist/source/use_sorted_package_json/sorters/helpers.rs`:
- Around line 274-320: The function uniq_and_sort_array currently moves all
non-string elements to the end (non_string_elements) which changes their
relative positions in mixed-type arrays; instead detect mixed arrays and bail
out to avoid reordering: in uniq_and_sort_array, after collecting string_values
and non_string_elements, if both collections are non-empty (i.e., the array
contains at least one string and at least one non-string), return None (do not
modify) so positions are preserved; keep this check before deduping/sorting and
before calling rebuild_array.
| Self::HtmlEslint(_) => 38, | ||
| Self::EslintE18e(_) => 38, | ||
| Self::EslintBetterTailwindcss(_) => 39, | ||
| Self::SortPackageJson => 40, |
There was a problem hiding this comment.
Duplicate variant_index for HtmlEslint and EslintE18e.
Both HtmlEslint (line 294) and EslintE18e (line 295) return index 38. This could cause non-deterministic ordering when comparing rule sources. One of these should be renumbered (e.g., EslintE18e => 39, then bump subsequent indices).
Proposed fix
Self::HtmlEslint(_) => 38,
- Self::EslintE18e(_) => 38,
- Self::EslintBetterTailwindcss(_) => 39,
- Self::SortPackageJson => 40,
+ Self::EslintE18e(_) => 39,
+ Self::EslintBetterTailwindcss(_) => 40,
+ Self::SortPackageJson => 41,📝 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.
| Self::HtmlEslint(_) => 38, | |
| Self::EslintE18e(_) => 38, | |
| Self::EslintBetterTailwindcss(_) => 39, | |
| Self::SortPackageJson => 40, | |
| Self::HtmlEslint(_) => 38, | |
| Self::EslintE18e(_) => 39, | |
| Self::EslintBetterTailwindcss(_) => 40, | |
| Self::SortPackageJson => 41, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/biome_analyze/src/rule.rs` around lines 294 - 297, The variant_index
mapping currently assigns the same index 38 to Self::HtmlEslint and
Self::EslintE18e, causing duplicate indices; update the mapping so each enum
variant has a unique sequential index by changing Self::EslintE18e to return 39
(and increment subsequent indices accordingly—e.g., change
Self::EslintBetterTailwindcss to 40 and Self::SortPackageJson to 41) in the
function/match that implements variant_index to restore deterministic ordering.
| // Skip package.json files - they have specialized sorting via organizePackageJson | ||
| let path = ctx.file_path(); | ||
|
|
There was a problem hiding this comment.
Comment says “skip”, but we don’t
Line 214 says we skip package.json and names “organizePackageJson”, but the code only adds a note and the action is useSortedPackageJson. Either implement the skip or update the comment to match the behaviour/name.
Suggested tweak
- // Skip package.json files - they have specialized sorting via organizePackageJson
+ // package.json files have specialised sorting via useSortedPackageJson; add a note.📝 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.
| // Skip package.json files - they have specialized sorting via organizePackageJson | |
| let path = ctx.file_path(); | |
| // package.json files have specialised sorting via useSortedPackageJson; add a note. | |
| let path = ctx.file_path(); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/biome_json_analyze/src/assist/source/use_sorted_keys.rs` around lines
214 - 216, The comment incorrectly states we "Skip package.json files - they
have specialized sorting via organizePackageJson" while the code only retrieves
the path (ctx.file_path()) and proceeds with the useSortedPackageJson action;
either implement an actual skip or update the comment to match behavior — I
recommend updating the comment to something like "Handle package.json with
useSortedPackageJson note" or, if you want to skip, add a guard that checks the
filename (e.g., if ctx.file_path().file_name() == Some("package.json")) and
branch to organizePackageJson or return early; reference use_sorted_keys.rs,
ctx.file_path(), organizePackageJson and useSortedPackageJson when making the
change.
| /// Remove duplicates and sort array alphabetically | ||
| pub fn uniq_and_sort_array(array: &AnyJsonValue) -> Option<AnyJsonValue> { | ||
| let array_value = array.as_json_array_value()?; | ||
| let elements = array_value.elements(); | ||
|
|
||
| let mut string_values: Vec<(TokenText, AnyJsonValue)> = Vec::new(); | ||
| let mut non_string_elements = Vec::new(); | ||
|
|
||
| for element in (&elements).into_iter().flatten() { | ||
| if let Some(string_val) = element.as_json_string_value() | ||
| && let Ok(text) = string_val.inner_string_text() | ||
| { | ||
| string_values.push((text, element.clone())); | ||
| } else { | ||
| non_string_elements.push(element.clone()); | ||
| } | ||
| } | ||
|
|
||
| // Remove duplicates and sort | ||
| let mut seen: std::collections::HashSet<TokenText> = std::collections::HashSet::new(); | ||
| let mut unique_sorted: Vec<(TokenText, AnyJsonValue)> = string_values | ||
| .into_iter() | ||
| .filter(|(s, _)| seen.insert(s.clone())) | ||
| .collect(); | ||
| unique_sorted.sort_by(|a, b| a.0.cmp(&b.0)); | ||
|
|
||
| let original_count = elements.iter().count(); | ||
| let new_count = unique_sorted.len() + non_string_elements.len(); | ||
|
|
||
| // Check if order changed | ||
| let current_strings: Vec<TokenText> = elements | ||
| .iter() | ||
| .filter_map(|e| e.ok()?.as_json_string_value()?.inner_string_text().ok()) | ||
| .collect(); | ||
| let sorted_strings: Vec<&TokenText> = unique_sorted.iter().map(|(s, _)| s).collect(); | ||
|
|
||
| if original_count == new_count && current_strings.iter().eq(sorted_strings.iter().copied()) { | ||
| return None; | ||
| } | ||
|
|
||
| let mut final_elements = Vec::new(); | ||
| for (_, elem) in unique_sorted { | ||
| final_elements.push(elem); | ||
| } | ||
| final_elements.extend(non_string_elements); | ||
|
|
||
| rebuild_array(array_value, final_elements) |
There was a problem hiding this comment.
Avoid reordering non‑string array entries.
When a mixed array is present, non‑string items are pushed to the end whenever sorting/dedup happens, which changes their relative order. If mixed arrays are possible, consider bailing out or preserving positions.
🛠️ Suggested conservative guard
for element in (&elements).into_iter().flatten() {
if let Some(string_val) = element.as_json_string_value()
&& let Ok(text) = string_val.inner_string_text()
{
string_values.push((text, element.clone()));
} else {
non_string_elements.push(element.clone());
}
}
+
+ if !non_string_elements.is_empty() {
+ return None;
+ }📝 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.
| /// Remove duplicates and sort array alphabetically | |
| pub fn uniq_and_sort_array(array: &AnyJsonValue) -> Option<AnyJsonValue> { | |
| let array_value = array.as_json_array_value()?; | |
| let elements = array_value.elements(); | |
| let mut string_values: Vec<(TokenText, AnyJsonValue)> = Vec::new(); | |
| let mut non_string_elements = Vec::new(); | |
| for element in (&elements).into_iter().flatten() { | |
| if let Some(string_val) = element.as_json_string_value() | |
| && let Ok(text) = string_val.inner_string_text() | |
| { | |
| string_values.push((text, element.clone())); | |
| } else { | |
| non_string_elements.push(element.clone()); | |
| } | |
| } | |
| // Remove duplicates and sort | |
| let mut seen: std::collections::HashSet<TokenText> = std::collections::HashSet::new(); | |
| let mut unique_sorted: Vec<(TokenText, AnyJsonValue)> = string_values | |
| .into_iter() | |
| .filter(|(s, _)| seen.insert(s.clone())) | |
| .collect(); | |
| unique_sorted.sort_by(|a, b| a.0.cmp(&b.0)); | |
| let original_count = elements.iter().count(); | |
| let new_count = unique_sorted.len() + non_string_elements.len(); | |
| // Check if order changed | |
| let current_strings: Vec<TokenText> = elements | |
| .iter() | |
| .filter_map(|e| e.ok()?.as_json_string_value()?.inner_string_text().ok()) | |
| .collect(); | |
| let sorted_strings: Vec<&TokenText> = unique_sorted.iter().map(|(s, _)| s).collect(); | |
| if original_count == new_count && current_strings.iter().eq(sorted_strings.iter().copied()) { | |
| return None; | |
| } | |
| let mut final_elements = Vec::new(); | |
| for (_, elem) in unique_sorted { | |
| final_elements.push(elem); | |
| } | |
| final_elements.extend(non_string_elements); | |
| rebuild_array(array_value, final_elements) | |
| /// Remove duplicates and sort array alphabetically | |
| pub fn uniq_and_sort_array(array: &AnyJsonValue) -> Option<AnyJsonValue> { | |
| let array_value = array.as_json_array_value()?; | |
| let elements = array_value.elements(); | |
| let mut string_values: Vec<(TokenText, AnyJsonValue)> = Vec::new(); | |
| let mut non_string_elements = Vec::new(); | |
| for element in (&elements).into_iter().flatten() { | |
| if let Some(string_val) = element.as_json_string_value() | |
| && let Ok(text) = string_val.inner_string_text() | |
| { | |
| string_values.push((text, element.clone())); | |
| } else { | |
| non_string_elements.push(element.clone()); | |
| } | |
| } | |
| if !non_string_elements.is_empty() { | |
| return None; | |
| } | |
| // Remove duplicates and sort | |
| let mut seen: std::collections::HashSet<TokenText> = std::collections::HashSet::new(); | |
| let mut unique_sorted: Vec<(TokenText, AnyJsonValue)> = string_values | |
| .into_iter() | |
| .filter(|(s, _)| seen.insert(s.clone())) | |
| .collect(); | |
| unique_sorted.sort_by(|a, b| a.0.cmp(&b.0)); | |
| let original_count = elements.iter().count(); | |
| let new_count = unique_sorted.len() + non_string_elements.len(); | |
| // Check if order changed | |
| let current_strings: Vec<TokenText> = elements | |
| .iter() | |
| .filter_map(|e| e.ok()?.as_json_string_value()?.inner_string_text().ok()) | |
| .collect(); | |
| let sorted_strings: Vec<&TokenText> = unique_sorted.iter().map(|(s, _)| s).collect(); | |
| if original_count == new_count && current_strings.iter().eq(sorted_strings.iter().copied()) { | |
| return None; | |
| } | |
| let mut final_elements = Vec::new(); | |
| for (_, elem) in unique_sorted { | |
| final_elements.push(elem); | |
| } | |
| final_elements.extend(non_string_elements); | |
| rebuild_array(array_value, final_elements) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@crates/biome_json_analyze/src/assist/source/use_sorted_package_json/sorters/helpers.rs`
around lines 274 - 320, The function uniq_and_sort_array currently moves all
non-string elements to the end (non_string_elements) which changes their
relative positions in mixed-type arrays; instead detect mixed arrays and bail
out to avoid reordering: in uniq_and_sort_array, after collecting string_values
and non_string_elements, if both collections are non-empty (i.e., the array
contains at least one string and at least one non-string), return None (do not
modify) so positions are preserved; keep this check before deduping/sorting and
before calling rebuild_array.
Summary
Superseeds #8659 with all the applied feedback from us.
I changed the API signature of some functions. Some function were returning
Option<Result<>>, which is an awkward, and useless API. Now it resultsOption.With the help of a coding agent:
Test Plan
All tests should pass
Docs