From 4baebefa81b497bf93965d7d9ff5a787a7456da4 Mon Sep 17 00:00:00 2001 From: leaysgur <6259812+leaysgur@users.noreply.github.com> Date: Sat, 14 Feb 2026 14:12:48 +0000 Subject: [PATCH] feat(formatter/sort_imports): Support `{ newlinesBetween: bool }` inside `groups` (#19358) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #17830 Introduce new variant for `groups` like: ```json // "newlinesBetween": true, // Default "groups": [ ["value-builtin", "value-external"], { "newlinesBetween": false }, // 👈🏻 "value-parent", "value-sibling" ] ``` This allows you to specify line breaks globally between groups while reverting them at any position. Conversely, you can also add line breaks at any position while specifying `newlinesBetween: false`. --- apps/oxfmt/src-js/index.ts | 5 +- apps/oxfmt/src/core/oxfmtrc.rs | 144 +++++++++- crates/oxc_formatter/examples/sort_imports.rs | 1 + .../src/ir_transform/sort_imports/mod.rs | 52 +++- .../src/ir_transform/sort_imports/options.rs | 5 + .../oxc_formatter/tests/ir_transform/mod.rs | 61 +++-- .../tests/ir_transform/sort_imports/mod.rs | 1 + .../sort_imports/newlines_between_override.rs | 258 ++++++++++++++++++ napi/playground/src/lib.rs | 1 + npm/oxfmt/configuration_schema.json | 30 +- .../src/snapshots/schema_json.snap | 30 +- .../src/snapshots/schema_markdown.snap | 16 -- 12 files changed, 544 insertions(+), 60 deletions(-) create mode 100644 crates/oxc_formatter/tests/ir_transform/sort_imports/newlines_between_override.rs diff --git a/apps/oxfmt/src-js/index.ts b/apps/oxfmt/src-js/index.ts index 6fc4d0a074069..6b11a21cbfbe5 100644 --- a/apps/oxfmt/src-js/index.ts +++ b/apps/oxfmt/src-js/index.ts @@ -123,9 +123,10 @@ export type SortImportsOptions = { /** * Groups configuration for organizing imports. * Each array element represents a group, and multiple group names in the same array are treated as one. - * Accepts both `string` and `string[]` as group elements. + * Accepts `string`, `string[]`, or `{ newlinesBetween: boolean }` marker objects. + * Marker objects override the global `newlinesBetween` setting for the boundary between the adjacent groups. */ - groups?: (string | string[])[]; + groups?: (string | string[] | { newlinesBetween: boolean })[]; /** Define custom groups for matching specific imports. */ customGroups?: { groupName: string; diff --git a/apps/oxfmt/src/core/oxfmtrc.rs b/apps/oxfmt/src/core/oxfmtrc.rs index b114be2f5e83d..30352ea3961a9 100644 --- a/apps/oxfmt/src/core/oxfmtrc.rs +++ b/apps/oxfmt/src/core/oxfmtrc.rs @@ -420,7 +420,48 @@ impl FormatConfig { sort_imports.internal_pattern = v; } if let Some(v) = config.groups { - sort_imports.groups = v.into_iter().map(SortGroupItemConfig::into_vec).collect(); + let mut groups = Vec::new(); + let mut newline_boundary_overrides: Vec> = Vec::new(); + let mut pending_override: Option = None; + + for item in v { + match item { + SortGroupItemConfig::NewlinesBetween(marker) => { + if groups.is_empty() { + return Err("Invalid `sortImports` configuration: `{ \"newlinesBetween\" }` marker cannot appear at the start of `groups`".to_string()); + } + if pending_override.is_some() { + return Err("Invalid `sortImports` configuration: consecutive `{ \"newlinesBetween\" }` markers are not allowed in `groups`".to_string()); + } + pending_override = Some(marker.newlines_between); + } + other => { + if !groups.is_empty() { + // Record the boundary between the previous group and this one. + // `pending_override` is + // - `Some(bool)` if a marker preceded this group + // - or `None` (= use global `newlines_between`) otherwise + // For the very first group (`groups.is_empty()`), + // there is no preceding boundary, so we skip this entirely. + newline_boundary_overrides.push(pending_override.take()); + } + groups.push(other.into_vec()); + } + } + } + + if pending_override.is_some() { + return Err("Invalid `sortImports` configuration: `{ \"newlinesBetween\" }` marker cannot appear at the end of `groups`".to_string()); + } + + sort_imports.groups = groups; + sort_imports.newline_boundary_overrides = newline_boundary_overrides; + } + + if sort_imports.partition_by_newline + && sort_imports.newline_boundary_overrides.iter().any(Option::is_some) + { + return Err("Invalid `sortImports` configuration: `partitionByNewline` and per-group `{ \"newlinesBetween\" }` markers cannot be used together".to_string()); } if let Some(v) = config.custom_groups { sort_imports.custom_groups = v @@ -698,15 +739,30 @@ pub enum SortOrderConfig { #[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)] #[serde(untagged)] pub enum SortGroupItemConfig { + /// A `{ "newlinesBetween": bool }` marker object that overrides the global `newlinesBetween` + /// setting for the boundary between the previous and next groups. + NewlinesBetween(NewlinesBetweenMarker), + /// A single group name string (e.g. `"value-builtin"`). Single(String), + /// Multiple group names treated as one group (e.g. `["value-builtin", "value-external"]`). Multiple(Vec), } +/// A marker object for overriding `newlinesBetween` at a specific group boundary. +#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)] +#[serde(rename_all = "camelCase")] +pub struct NewlinesBetweenMarker { + pub newlines_between: bool, +} + impl SortGroupItemConfig { fn into_vec(self) -> Vec { match self { Self::Single(s) => vec![s], Self::Multiple(v) => v, + Self::NewlinesBetween(_) => { + unreachable!("NewlinesBetween markers should be handled before calling into_vec") + } } } } @@ -1206,6 +1262,92 @@ mod tests { assert_eq!(sort_imports.groups[0], vec!["builtin".to_string()]); assert_eq!(sort_imports.groups[1], vec!["external".to_string(), "internal".to_string()]); assert_eq!(sort_imports.groups[4], vec!["index".to_string()]); + + // Test groups with newlinesBetween overrides + let config: FormatConfig = serde_json::from_str( + r#"{ + "experimentalSortImports": { + "groups": [ + "builtin", + { "newlinesBetween": false }, + "external", + "parent" + ] + } + }"#, + ) + .unwrap(); + let oxfmt_options = config.into_oxfmt_options().unwrap(); + let sort_imports = oxfmt_options.format_options.experimental_sort_imports.unwrap(); + assert_eq!(sort_imports.groups.len(), 3); + assert_eq!(sort_imports.groups[0], vec!["builtin".to_string()]); + assert_eq!(sort_imports.groups[1], vec!["external".to_string()]); + assert_eq!(sort_imports.groups[2], vec!["parent".to_string()]); + assert_eq!(sort_imports.newline_boundary_overrides.len(), 2); + assert_eq!(sort_imports.newline_boundary_overrides[0], Some(false)); + assert_eq!(sort_imports.newline_boundary_overrides[1], None); + + // Test error: newlinesBetween at start of groups + let config: FormatConfig = serde_json::from_str( + r#"{ + "experimentalSortImports": { + "groups": [ + { "newlinesBetween": false }, + "builtin", + "external" + ] + } + }"#, + ) + .unwrap(); + assert!(config.into_oxfmt_options().is_err_and(|e| e.contains("start"))); + + // Test error: newlinesBetween at end of groups + let config: FormatConfig = serde_json::from_str( + r#"{ + "experimentalSortImports": { + "groups": [ + "builtin", + "external", + { "newlinesBetween": true } + ] + } + }"#, + ) + .unwrap(); + assert!(config.into_oxfmt_options().is_err_and(|e| e.contains("end"))); + + // Test error: consecutive newlinesBetween markers + let config: FormatConfig = serde_json::from_str( + r#"{ + "experimentalSortImports": { + "groups": [ + "builtin", + { "newlinesBetween": false }, + { "newlinesBetween": true }, + "external" + ] + } + }"#, + ) + .unwrap(); + assert!(config.into_oxfmt_options().is_err_and(|e| e.contains("consecutive"))); + + // Test error: partitionByNewline with per-group newlinesBetween markers + let config: FormatConfig = serde_json::from_str( + r#"{ + "experimentalSortImports": { + "partitionByNewline": true, + "groups": [ + "builtin", + { "newlinesBetween": false }, + "external" + ] + } + }"#, + ) + .unwrap(); + assert!(config.into_oxfmt_options().is_err_and(|e| e.contains("partitionByNewline"))); } } diff --git a/crates/oxc_formatter/examples/sort_imports.rs b/crates/oxc_formatter/examples/sort_imports.rs index cca790cc3ff64..2ec295eb4cf40 100644 --- a/crates/oxc_formatter/examples/sort_imports.rs +++ b/crates/oxc_formatter/examples/sort_imports.rs @@ -34,6 +34,7 @@ fn main() -> Result<(), String> { internal_pattern: default_internal_patterns(), groups: default_groups(), custom_groups: vec![], + newline_boundary_overrides: vec![], }; // Read source file diff --git a/crates/oxc_formatter/src/ir_transform/sort_imports/mod.rs b/crates/oxc_formatter/src/ir_transform/sort_imports/mod.rs index cc2d0ec45735e..080d09c4ab889 100644 --- a/crates/oxc_formatter/src/ir_transform/sort_imports/mod.rs +++ b/crates/oxc_formatter/src/ir_transform/sort_imports/mod.rs @@ -262,17 +262,22 @@ impl SortImportsTransform { // Insert newline when: // 1. Group changes // 2. Previous import was not ignored (don't insert after ignored) - if options.newlines_between { - let current_group_idx = sorted_import.group_idx; - if let Some(prev_idx) = prev_group_idx - && prev_idx != current_group_idx - && !prev_was_ignored - { - next_elements.push(FormatElement::Line(LineMode::Empty)); - } - prev_group_idx = Some(current_group_idx); - prev_was_ignored = sorted_import.is_ignored; + // 3. The boundary override (or global `newlines_between`) says to insert + let current_group_idx = sorted_import.group_idx; + if let Some(prev_idx) = prev_group_idx + && prev_idx != current_group_idx + && !prev_was_ignored + && should_insert_newline_between( + options.newlines_between, + &options.newline_boundary_overrides, + prev_idx, + current_group_idx, + ) + { + next_elements.push(FormatElement::Line(LineMode::Empty)); } + prev_group_idx = Some(current_group_idx); + prev_was_ignored = sorted_import.is_ignored; // Output leading lines and import line for line in &sorted_import.leading_lines { @@ -332,3 +337,30 @@ impl SortImportsTransform { Some(next_elements) } } + +/// Resolve whether a blank line should be inserted between two group indices. +/// Checks each boundary between `prev_group_idx` and `current_group_idx`, +/// using per-boundary overrides if available, otherwise the global `newlines_between`. +/// +/// When groups are skipped (i.e. no imports match an intermediate group), +/// multiple boundaries are evaluated with OR semantics. +/// If any single boundary in the range resolves to `true`, a blank line is inserted. +fn should_insert_newline_between( + global_newlines_between: bool, + newline_boundary_overrides: &[Option], + prev_group_idx: usize, + current_group_idx: usize, +) -> bool { + if newline_boundary_overrides.is_empty() { + return global_newlines_between; + } + + for idx in prev_group_idx..current_group_idx { + if newline_boundary_overrides.get(idx).copied().flatten().unwrap_or(global_newlines_between) + { + return true; + } + } + + false +} diff --git a/crates/oxc_formatter/src/ir_transform/sort_imports/options.rs b/crates/oxc_formatter/src/ir_transform/sort_imports/options.rs index 8f429eb969157..a8136e57e37f5 100644 --- a/crates/oxc_formatter/src/ir_transform/sort_imports/options.rs +++ b/crates/oxc_formatter/src/ir_transform/sort_imports/options.rs @@ -36,6 +36,10 @@ pub struct SortImportsOptions { /// Define your own groups for matching very specific imports. /// Default is `[]`. pub custom_groups: Vec, + /// Per-boundary newline overrides. + /// `newline_boundary_overrides[i]` = override for boundary between `groups[i]` and `groups[i+1]`. + /// `None` means "use global `newlines_between`". + pub newline_boundary_overrides: Vec>, } impl Default for SortImportsOptions { @@ -50,6 +54,7 @@ impl Default for SortImportsOptions { internal_pattern: default_internal_patterns(), groups: default_groups(), custom_groups: vec![], + newline_boundary_overrides: vec![], } } } diff --git a/crates/oxc_formatter/tests/ir_transform/mod.rs b/crates/oxc_formatter/tests/ir_transform/mod.rs index 996d7942a2cbf..9d7ddf9878a2e 100644 --- a/crates/oxc_formatter/tests/ir_transform/mod.rs +++ b/crates/oxc_formatter/tests/ir_transform/mod.rs @@ -78,43 +78,49 @@ struct TestSortImportsConfig { newlines_between: Option, internal_pattern: Option>, #[serde(default, deserialize_with = "deserialize_groups")] - groups: Option>>, + groups: Option, custom_groups: Option>, } -fn deserialize_groups<'de, D>(deserializer: D) -> Result>>, D::Error> +#[derive(Debug, Default)] +struct ParsedGroups { + groups: Vec>, + newline_boundary_overrides: Vec>, +} + +fn deserialize_groups<'de, D>(deserializer: D) -> Result, D::Error> where D: serde::Deserializer<'de>, { - use serde::de::Error; use serde_json::Value; - let value: Option = Option::deserialize(deserializer)?; - match value { - None => Ok(None), - Some(Value::Array(arr)) => { - let mut groups = Vec::new(); - for item in arr { - match item { - Value::String(s) => groups.push(vec![s]), - Value::Array(group_arr) => { - let mut group = Vec::new(); - for g in group_arr { - if let Value::String(s) = g { - group.push(s); - } else { - return Err(D::Error::custom("groups must contain strings")); - } - } - groups.push(group); - } - _ => return Err(D::Error::custom("groups must be strings or arrays")), - } + let Some(Value::Array(arr)) = Option::deserialize(deserializer)? else { + return Ok(None); + }; + + let mut groups = Vec::new(); + let mut newline_boundary_overrides: Vec> = Vec::new(); + let mut pending_override: Option = None; + + for item in arr { + if let Value::Object(obj) = item { + pending_override = obj.get("newlinesBetween").and_then(Value::as_bool); + } else { + if !groups.is_empty() { + newline_boundary_overrides.push(pending_override.take()); } - Ok(Some(groups)) + let group = match item { + Value::String(s) => vec![s], + Value::Array(a) => { + a.into_iter().filter_map(|v| v.as_str().map(String::from)).collect() + } + _ => continue, + }; + groups.push(group); } - Some(_) => Err(D::Error::custom("groups must be an array")), } + + Ok(Some(ParsedGroups { groups, newline_boundary_overrides })) } fn parse_test_config(json: &str) -> FormatOptions { @@ -154,7 +160,8 @@ fn parse_test_config(json: &str) -> FormatOptions { sort_imports.internal_pattern = v; } if let Some(v) = sort_config.groups { - sort_imports.groups = v; + sort_imports.groups = v.groups; + sort_imports.newline_boundary_overrides = v.newline_boundary_overrides; } if let Some(v) = sort_config.custom_groups { sort_imports.custom_groups = v diff --git a/crates/oxc_formatter/tests/ir_transform/sort_imports/mod.rs b/crates/oxc_formatter/tests/ir_transform/sort_imports/mod.rs index 7be0ce7dcb92b..56f6a0c7fe62e 100644 --- a/crates/oxc_formatter/tests/ir_transform/sort_imports/mod.rs +++ b/crates/oxc_formatter/tests/ir_transform/sort_imports/mod.rs @@ -1,3 +1,4 @@ mod basic; mod custom_groups; mod groups; +mod newlines_between_override; diff --git a/crates/oxc_formatter/tests/ir_transform/sort_imports/newlines_between_override.rs b/crates/oxc_formatter/tests/ir_transform/sort_imports/newlines_between_override.rs new file mode 100644 index 0000000000000..c4c798b7a370d --- /dev/null +++ b/crates/oxc_formatter/tests/ir_transform/sort_imports/newlines_between_override.rs @@ -0,0 +1,258 @@ +use super::super::assert_format; + +#[test] +fn global_true_with_override_false_suppresses_blank_line() { + // Global newlinesBetween: true, but override suppresses blank line between + // value-builtin/value-external and value-parent + assert_format( + r#" +import path from "path"; +import { foo } from "../foo"; +import { bar } from "./bar"; +import { baz } from "baz"; +"#, + r#"{ + "experimentalSortImports": { + "newlinesBetween": true, + "groups": [ + ["value-builtin", "value-external"], + { "newlinesBetween": false }, + "value-parent", + "value-sibling" + ] + } + }"#, + r#" +import { baz } from "baz"; +import path from "path"; +import { foo } from "../foo"; + +import { bar } from "./bar"; +"#, + ); +} + +#[test] +fn global_false_with_override_true_inserts_blank_line() { + // Global newlinesBetween: false, but override inserts blank line between + // value-builtin/value-external and value-parent + assert_format( + r#" +import path from "path"; +import { foo } from "../foo"; +import { bar } from "./bar"; +import { baz } from "baz"; +"#, + r#"{ + "experimentalSortImports": { + "newlinesBetween": false, + "groups": [ + ["value-builtin", "value-external"], + { "newlinesBetween": true }, + "value-parent", + "value-sibling" + ] + } + }"#, + r#" +import { baz } from "baz"; +import path from "path"; + +import { foo } from "../foo"; +import { bar } from "./bar"; +"#, + ); +} + +#[test] +fn multiple_overrides() { + // Global newlinesBetween: true with multiple overrides + assert_format( + r#" +import path from "path"; +import type { T } from "t"; +import { foo } from "../foo"; +import { bar } from "./bar"; +import { baz } from "baz"; +"#, + r#"{ + "experimentalSortImports": { + "newlinesBetween": true, + "groups": [ + "type-import", + { "newlinesBetween": false }, + ["value-builtin", "value-external"], + "value-parent", + { "newlinesBetween": false }, + "value-sibling" + ] + } + }"#, + r#" +import type { T } from "t"; +import { baz } from "baz"; +import path from "path"; + +import { foo } from "../foo"; +import { bar } from "./bar"; +"#, + ); +} + +#[test] +fn override_with_subgroups() { + // Override between a subgroup and a single group + assert_format( + r#" +import path from "path"; +import { foo } from "../foo"; +import { bar } from "./bar"; +import { baz } from "baz"; +"#, + r#"{ + "experimentalSortImports": { + "newlinesBetween": false, + "groups": [ + ["value-builtin", "value-external"], + { "newlinesBetween": true }, + ["value-parent", "value-sibling"] + ] + } + }"#, + r#" +import { baz } from "baz"; +import path from "path"; + +import { foo } from "../foo"; +import { bar } from "./bar"; +"#, + ); +} + +#[test] +fn all_overrides_false_is_same_as_global_false() { + // All boundaries explicitly set to false + assert_format( + r#" +import path from "path"; +import { foo } from "../foo"; +import { bar } from "./bar"; +import { baz } from "baz"; +"#, + r#"{ + "experimentalSortImports": { + "newlinesBetween": true, + "groups": [ + ["value-builtin", "value-external"], + { "newlinesBetween": false }, + "value-parent", + { "newlinesBetween": false }, + "value-sibling" + ] + } + }"#, + r#" +import { baz } from "baz"; +import path from "path"; +import { foo } from "../foo"; +import { bar } from "./bar"; +"#, + ); +} + +#[test] +fn override_does_not_affect_non_adjacent_groups() { + // Override between groups 0 and 1, but groups 1 and 2 use global (true) + assert_format( + r#" +import path from "path"; +import type { T } from "t"; +import { foo } from "../foo"; +import { baz } from "baz"; +"#, + r#"{ + "experimentalSortImports": { + "newlinesBetween": true, + "groups": [ + "type-import", + { "newlinesBetween": false }, + ["value-builtin", "value-external"], + "value-parent" + ] + } + }"#, + r#" +import type { T } from "t"; +import { baz } from "baz"; +import path from "path"; + +import { foo } from "../foo"; +"#, + ); +} + +#[test] +fn skipped_intermediate_groups_with_override_false() { + // Groups: type-import, value-builtin, value-internal, value-parent + // No imports match value-internal, so group index jumps from 1 to 3. + // Both intermediate overrides are false, so no blank line. + assert_format( + r#" +import path from "path"; +import { foo } from "../foo"; +import type { T } from "t"; +"#, + r#"{ + "experimentalSortImports": { + "newlinesBetween": true, + "groups": [ + "type-import", + { "newlinesBetween": false }, + "value-builtin", + { "newlinesBetween": false }, + "value-internal", + { "newlinesBetween": false }, + "value-parent" + ] + } + }"#, + r#" +import type { T } from "t"; +import path from "path"; +import { foo } from "../foo"; +"#, + ); +} + +#[test] +fn skipped_intermediate_groups_with_mixed_overrides() { + // Groups: type-import, value-builtin, value-internal, value-parent + // No imports match value-internal, so group index jumps from 1 to 3. + // Overrides: false between type-import/value-builtin, true between value-builtin/value-internal. + // Since one boundary in the path is true, a blank line IS inserted. + assert_format( + r#" +import path from "path"; +import { foo } from "../foo"; +import type { T } from "t"; +"#, + r#"{ + "experimentalSortImports": { + "newlinesBetween": false, + "groups": [ + "type-import", + "value-builtin", + { "newlinesBetween": true }, + "value-internal", + "value-parent" + ] + } + }"#, + r#" +import type { T } from "t"; +import path from "path"; + +import { foo } from "../foo"; +"#, + ); +} diff --git a/napi/playground/src/lib.rs b/napi/playground/src/lib.rs index 68360af92bd24..bed9607dd9a80 100644 --- a/napi/playground/src/lib.rs +++ b/napi/playground/src/lib.rs @@ -529,6 +529,7 @@ impl Oxc { .unwrap_or_else(default_internal_patterns), groups: sort_imports_config.groups.clone().unwrap_or_else(default_groups), custom_groups: vec![], + newline_boundary_overrides: vec![], }); } diff --git a/npm/oxfmt/configuration_schema.json b/npm/oxfmt/configuration_schema.json index daeb3353f9d4a..80eaf309894d6 100644 --- a/npm/oxfmt/configuration_schema.json +++ b/npm/oxfmt/configuration_schema.json @@ -552,6 +552,19 @@ "ignore" ] }, + "NewlinesBetweenMarker": { + "description": "A marker object for overriding `newlinesBetween` at a specific group boundary.", + "type": "object", + "required": [ + "newlinesBetween" + ], + "properties": { + "newlinesBetween": { + "type": "boolean" + } + }, + "markdownDescription": "A marker object for overriding `newlinesBetween` at a specific group boundary." + }, "ObjectWrapConfig": { "type": "string", "enum": [ @@ -615,13 +628,26 @@ "SortGroupItemConfig": { "anyOf": [ { - "type": "string" + "description": "A `{ \"newlinesBetween\": bool }` marker object that overrides the global `newlinesBetween`\nsetting for the boundary between the previous and next groups.", + "allOf": [ + { + "$ref": "#/definitions/NewlinesBetweenMarker" + } + ], + "markdownDescription": "A `{ \"newlinesBetween\": bool }` marker object that overrides the global `newlinesBetween`\nsetting for the boundary between the previous and next groups." }, { + "description": "A single group name string (e.g. `\"value-builtin\"`).", + "type": "string", + "markdownDescription": "A single group name string (e.g. `\"value-builtin\"`)." + }, + { + "description": "Multiple group names treated as one group (e.g. `[\"value-builtin\", \"value-external\"]`).", "type": "array", "items": { "type": "string" - } + }, + "markdownDescription": "Multiple group names treated as one group (e.g. `[\"value-builtin\", \"value-external\"]`)." } ] }, diff --git a/tasks/website_formatter/src/snapshots/schema_json.snap b/tasks/website_formatter/src/snapshots/schema_json.snap index 315bd093bda04..657a7b1937c76 100644 --- a/tasks/website_formatter/src/snapshots/schema_json.snap +++ b/tasks/website_formatter/src/snapshots/schema_json.snap @@ -556,6 +556,19 @@ expression: json "ignore" ] }, + "NewlinesBetweenMarker": { + "description": "A marker object for overriding `newlinesBetween` at a specific group boundary.", + "type": "object", + "required": [ + "newlinesBetween" + ], + "properties": { + "newlinesBetween": { + "type": "boolean" + } + }, + "markdownDescription": "A marker object for overriding `newlinesBetween` at a specific group boundary." + }, "ObjectWrapConfig": { "type": "string", "enum": [ @@ -619,13 +632,26 @@ expression: json "SortGroupItemConfig": { "anyOf": [ { - "type": "string" + "description": "A `{ \"newlinesBetween\": bool }` marker object that overrides the global `newlinesBetween`\nsetting for the boundary between the previous and next groups.", + "allOf": [ + { + "$ref": "#/definitions/NewlinesBetweenMarker" + } + ], + "markdownDescription": "A `{ \"newlinesBetween\": bool }` marker object that overrides the global `newlinesBetween`\nsetting for the boundary between the previous and next groups." }, { + "description": "A single group name string (e.g. `\"value-builtin\"`).", + "type": "string", + "markdownDescription": "A single group name string (e.g. `\"value-builtin\"`)." + }, + { + "description": "Multiple group names treated as one group (e.g. `[\"value-builtin\", \"value-external\"]`).", "type": "array", "items": { "type": "string" - } + }, + "markdownDescription": "Multiple group names treated as one group (e.g. `[\"value-builtin\", \"value-external\"]`)." } ] }, diff --git a/tasks/website_formatter/src/snapshots/schema_markdown.snap b/tasks/website_formatter/src/snapshots/schema_markdown.snap index 0d359cbefe4f9..e66a318bcf0c9 100644 --- a/tasks/website_formatter/src/snapshots/schema_markdown.snap +++ b/tasks/website_formatter/src/snapshots/schema_markdown.snap @@ -211,14 +211,6 @@ type: `array | string` -##### experimentalSortImports.groups[n][n] - -type: `string` - - - - - ### experimentalSortImports.ignoreCase type: `boolean` @@ -720,14 +712,6 @@ type: `array | string` -######## overrides[n].options.experimentalSortImports.groups[n][n] - -type: `string` - - - - - ###### overrides[n].options.experimentalSortImports.ignoreCase type: `boolean`