Skip to content

feat(useNumericSeparators): add options for minimum digits and group length#9571

Open
dyc3 wants to merge 1 commit intonextfrom
dyc3/number-separator-options
Open

feat(useNumericSeparators): add options for minimum digits and group length#9571
dyc3 wants to merge 1 commit intonextfrom
dyc3/number-separator-options

Conversation

@dyc3
Copy link
Contributor

@dyc3 dyc3 commented Mar 21, 2026

Summary

I realize the way the options are documented is a little different than what we usually do. The agent came up with it, and tbh I think the more concise summary is more preferable than having valid and invalid cases for all 4 * 2 options.

Generated by opus 4.6

discussion: #9316

Test Plan

snapshots

Docs

@changeset-bot
Copy link

changeset-bot bot commented Mar 21, 2026

🦋 Changeset detected

Latest commit: bb3efae

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 14 packages
Name Type
@biomejs/biome Minor
@biomejs/cli-win32-x64 Minor
@biomejs/cli-win32-arm64 Minor
@biomejs/cli-darwin-x64 Minor
@biomejs/cli-darwin-arm64 Minor
@biomejs/cli-linux-x64 Minor
@biomejs/cli-linux-arm64 Minor
@biomejs/cli-linux-x64-musl Minor
@biomejs/cli-linux-arm64-musl Minor
@biomejs/wasm-web Minor
@biomejs/wasm-bundler Minor
@biomejs/wasm-nodejs Minor
@biomejs/backend-jsonrpc Patch
@biomejs/js-api Major

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-Linter Area: linter L-JavaScript Language: JavaScript and super languages labels Mar 21, 2026
@codspeed-hq
Copy link

codspeed-hq bot commented Mar 21, 2026

Merging this PR will not alter performance

✅ 58 untouched benchmarks
⏩ 156 skipped benchmarks1


Comparing dyc3/number-separator-options (bb3efae) with next (d5913c9)

Open in CodSpeed

Footnotes

  1. 156 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.

@dyc3 dyc3 marked this pull request as ready for review March 21, 2026 16:15
@dyc3 dyc3 requested review from a team March 21, 2026 16:15
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 21, 2026

Walkthrough

This change introduces configurable options for the useNumericSeparators linter rule, enabling users to customise separator formatting per numeric literal type: binary, octal, decimal, and hexadecimal. Each type can independently specify minimumDigits and groupLength thresholds. The implementation includes rule option structs, migration support for ESLint's unicorn/numeric-separators-style rule, updated formatter logic, and comprehensive test cases validating both valid and invalid custom configurations across all numeric literal types.

Suggested reviewers

  • ematipico
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: adding configurable options for minimum digits and group length to the useNumericSeparators rule.
Description check ✅ Passed The description relates to the changeset by noting the documentation approach and acknowledging AI assistance, though it lacks detail about technical implementation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dyc3/number-separator-options

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
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: 1

🧹 Nitpick comments (1)
crates/biome_rule_options/src/use_numeric_separators.rs (1)

56-109: Consider reducing duplication in accessor methods.

Each accessor follows the same pattern. You could simplify by calling resolve() unconditionally on an unwrapped-or-default reference:

♻️ Optional refactor
     /// Returns `(min_digits, group_length)` for binary literals.
     pub fn binary(&self) -> (usize, usize) {
-        match &self.binary {
-            Some(opts) => opts.resolve(
-                Self::DEFAULT_BINARY_MIN_DIGITS,
-                Self::DEFAULT_BINARY_GROUP_LENGTH,
-            ),
-            None => (
-                Self::DEFAULT_BINARY_MIN_DIGITS as usize,
-                Self::DEFAULT_BINARY_GROUP_LENGTH as usize,
-            ),
-        }
+        self.binary
+            .as_ref()
+            .unwrap_or(&NumericLiteralSeparatorOptions::default())
+            .resolve(Self::DEFAULT_BINARY_MIN_DIGITS, Self::DEFAULT_BINARY_GROUP_LENGTH)
     }

That said, the explicit match is perfectly readable — feel free to keep it if you prefer the clarity.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_rule_options/src/use_numeric_separators.rs` around lines 56 -
109, The four accessor methods binary, octal, decimal, and hexadecimal duplicate
the same match/None pattern; refactor by centralizing the logic so each just
calls a single resolver. Add a small helper (e.g., resolve_numeric or
resolve_or_default) that accepts the Option (the fields used by
binary/octal/decimal/hexadecimal), the corresponding DEFAULT_*_MIN_DIGITS and
DEFAULT_*_GROUP_LENGTH values, and returns (usize, usize) by calling
opts.resolve(...) when Some or using the defaults when None; then have binary,
octal, decimal, and hexadecimal call that helper passing their respective
DEFAULT_* constants and the field so the resolve() invocation is not duplicated.
🤖 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_cli/src/execute/migrate/eslint_unicorn.rs`:
- Around line 84-98: The migration currently drops the ESLint
onlyIfContainsSeparator option silently; update the
NumericSeparatorsStyleOptions and EslintNumericSeparatorTypeOptions structs to
include an Option<bool> field named only_if_contains_separator (or similar) to
capture that flag, then modify the migration path to detect when
only_if_contains_separator is Some(true/false) and emit a migration notice
explaining Biome doesn't support it and the behavior will be different;
reference the structs NumericSeparatorsStyleOptions and
EslintNumericSeparatorTypeOptions and ensure the migration notice is produced
wherever these structs are parsed/converted so users see the delta.

---

Nitpick comments:
In `@crates/biome_rule_options/src/use_numeric_separators.rs`:
- Around line 56-109: The four accessor methods binary, octal, decimal, and
hexadecimal duplicate the same match/None pattern; refactor by centralizing the
logic so each just calls a single resolver. Add a small helper (e.g.,
resolve_numeric or resolve_or_default) that accepts the Option (the fields used
by binary/octal/decimal/hexadecimal), the corresponding DEFAULT_*_MIN_DIGITS and
DEFAULT_*_GROUP_LENGTH values, and returns (usize, usize) by calling
opts.resolve(...) when Some or using the defaults when None; then have binary,
octal, decimal, and hexadecimal call that helper passing their respective
DEFAULT_* constants and the field so the resolve() invocation is not duplicated.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d550d887-f987-44fc-909a-30ad207630d7

📥 Commits

Reviewing files that changed from the base of the PR and between d5913c9 and bb3efae.

⛔ Files ignored due to path filters (4)
  • crates/biome_js_analyze/tests/specs/style/useNumericSeparators/invalidCustomOptions.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/style/useNumericSeparators/validCustomOptions.js.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 (10)
  • .changeset/use-numeric-separators-options.md
  • crates/biome_cli/src/execute/migrate/eslint_eslint.rs
  • crates/biome_cli/src/execute/migrate/eslint_to_biome.rs
  • crates/biome_cli/src/execute/migrate/eslint_unicorn.rs
  • crates/biome_js_analyze/src/lint/style/use_numeric_separators.rs
  • crates/biome_js_analyze/tests/specs/style/useNumericSeparators/invalidCustomOptions.js
  • crates/biome_js_analyze/tests/specs/style/useNumericSeparators/invalidCustomOptions.options.json
  • crates/biome_js_analyze/tests/specs/style/useNumericSeparators/validCustomOptions.js
  • crates/biome_js_analyze/tests/specs/style/useNumericSeparators/validCustomOptions.options.json
  • crates/biome_rule_options/src/use_numeric_separators.rs

Comment on lines +84 to +98
/// Note: ESLint uses `number` for decimal literals, while Biome uses `decimal`.
/// The `onlyIfContainsSeparator` option is not supported and is ignored.
#[derive(Clone, Debug, Default, Deserializable)]
pub(crate) struct NumericSeparatorsStyleOptions {
number: EslintNumericSeparatorTypeOptions,
binary: EslintNumericSeparatorTypeOptions,
octal: EslintNumericSeparatorTypeOptions,
hexadecimal: EslintNumericSeparatorTypeOptions,
}

#[derive(Clone, Debug, Default, Deserializable)]
pub(crate) struct EslintNumericSeparatorTypeOptions {
minimum_digits: Option<u8>,
group_length: Option<u8>,
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In eslint-plugin-unicorn's numeric-separators-stylerule, isonlyIfContainsSeparator supported both at top-level and per numeric type?

💡 Result:

Yes. The rule supports onlyIfContainsSeparator both:

  • Top-level ({ onlyIfContainsSeparator: true }), applying as the default for all numeric types, and
  • Per numeric type (binary / octal / hexadecimal / number), overriding the top-level value for that type. The docs state this explicitly, and the implementation merges top-level into each type while allowing the per-type option to override it. (raw.githubusercontent.com)

Citations:


🏁 Script executed:

cat -n crates/biome_cli/src/execute/migrate/eslint_unicorn.rs | sed -n '111,130p'

Repository: biomejs/biome

Length of output: 837


🏁 Script executed:

rg -t rs "struct.*UseNumericSeparatorsOptions" -A 15

Repository: biomejs/biome

Length of output: 83


🏁 Script executed:

rg "struct.*UseNumericSeparatorsOptions" -A 15

Repository: biomejs/biome

Length of output: 1731


🏁 Script executed:

rg "struct.*NumericLiteralSeparatorOptions" -A 10

Repository: biomejs/biome

Length of output: 1137


Capture onlyIfContainsSeparator for migration notice—Biome doesn't support it yet, but silently dropping it can surprise users.

ESLint's rule supports this option at both top-level and per-type, but Biome's NumericLiteralSeparatorOptions doesn't include it. Rather than silently dropping it (lines 84–98 and 111–130), capture it in the structs and emit a migration notice when present, so the behaviour delta is explicit.

Suggested capture
 pub(crate) struct NumericSeparatorsStyleOptions {
+    only_if_contains_separator: Option<bool>,
     number: EslintNumericSeparatorTypeOptions,
     binary: EslintNumericSeparatorTypeOptions,
     octal: EslintNumericSeparatorTypeOptions,
     hexadecimal: EslintNumericSeparatorTypeOptions,
 }

 pub(crate) struct EslintNumericSeparatorTypeOptions {
+    only_if_contains_separator: Option<bool>,
     minimum_digits: Option<u8>,
     group_length: Option<u8>,
 }
📝 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
/// Note: ESLint uses `number` for decimal literals, while Biome uses `decimal`.
/// The `onlyIfContainsSeparator` option is not supported and is ignored.
#[derive(Clone, Debug, Default, Deserializable)]
pub(crate) struct NumericSeparatorsStyleOptions {
number: EslintNumericSeparatorTypeOptions,
binary: EslintNumericSeparatorTypeOptions,
octal: EslintNumericSeparatorTypeOptions,
hexadecimal: EslintNumericSeparatorTypeOptions,
}
#[derive(Clone, Debug, Default, Deserializable)]
pub(crate) struct EslintNumericSeparatorTypeOptions {
minimum_digits: Option<u8>,
group_length: Option<u8>,
}
/// Note: ESLint uses `number` for decimal literals, while Biome uses `decimal`.
/// The `onlyIfContainsSeparator` option is not supported and is ignored.
#[derive(Clone, Debug, Default, Deserializable)]
pub(crate) struct NumericSeparatorsStyleOptions {
only_if_contains_separator: Option<bool>,
number: EslintNumericSeparatorTypeOptions,
binary: EslintNumericSeparatorTypeOptions,
octal: EslintNumericSeparatorTypeOptions,
hexadecimal: EslintNumericSeparatorTypeOptions,
}
#[derive(Clone, Debug, Default, Deserializable)]
pub(crate) struct EslintNumericSeparatorTypeOptions {
only_if_contains_separator: Option<bool>,
minimum_digits: Option<u8>,
group_length: Option<u8>,
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_cli/src/execute/migrate/eslint_unicorn.rs` around lines 84 - 98,
The migration currently drops the ESLint onlyIfContainsSeparator option
silently; update the NumericSeparatorsStyleOptions and
EslintNumericSeparatorTypeOptions structs to include an Option<bool> field named
only_if_contains_separator (or similar) to capture that flag, then modify the
migration path to detect when only_if_contains_separator is Some(true/false) and
emit a migration notice explaining Biome doesn't support it and the behavior
will be different; reference the structs NumericSeparatorsStyleOptions and
EslintNumericSeparatorTypeOptions and ensure the migration notice is produced
wherever these structs are parsed/converted so users see the delta.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-CLI Area: CLI A-Linter Area: linter L-JavaScript Language: JavaScript and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant