diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index b0226d949..1d5e5e8e6 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -179,6 +179,7 @@ mod unicorn { pub mod no_useless_switch_case; pub mod no_zero_fractions; pub mod number_literal_case; + pub mod numeric_separators_style; pub mod prefer_add_event_listener; pub mod prefer_array_flat_map; pub mod prefer_array_some; @@ -357,6 +358,7 @@ oxc_macros::declare_all_lint_rules! { unicorn::no_useless_switch_case, unicorn::no_zero_fractions, unicorn::number_literal_case, + unicorn::numeric_separators_style, unicorn::prefer_add_event_listener, unicorn::prefer_array_flat_map, unicorn::prefer_blob_reading_methods, diff --git a/crates/oxc_linter/src/rules/unicorn/numeric_separators_style.rs b/crates/oxc_linter/src/rules/unicorn/numeric_separators_style.rs new file mode 100644 index 000000000..4dd3d3333 --- /dev/null +++ b/crates/oxc_linter/src/rules/unicorn/numeric_separators_style.rs @@ -0,0 +1,671 @@ +use lazy_static::lazy_static; +use oxc_ast::{ + ast::{BigintLiteral, NumberLiteral}, + AstKind, +}; +use oxc_diagnostics::{ + miette::{self, Diagnostic}, + thiserror::Error, +}; +use oxc_macros::declare_oxc_lint; +use oxc_span::Span; +use regex::Regex; + +use crate::{context::LintContext, fixer::Fix, rule::Rule, AstNode}; + +#[derive(Debug, Error, Diagnostic)] +#[error("eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value.")] +#[diagnostic( + severity(warning), + help("Group digits with numeric separators (_) so longer numbers are easier to read.") +)] +struct NumericSeparatorsStyleDiagnostic(#[label] pub Span); + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct NumericSeparatorsStyle { + only_if_contains_separator: bool, + hexadecimal: NumericBaseConfig, + binary: NumericBaseConfig, + octal: NumericBaseConfig, + number: NumericBaseConfig, +} +impl Default for NumericSeparatorsStyle { + fn default() -> Self { + Self { + only_if_contains_separator: false, + binary: NumericBaseConfig { group_length: 4, minimum_digits: 0 }, + hexadecimal: NumericBaseConfig { group_length: 2, minimum_digits: 0 }, + number: NumericBaseConfig { group_length: 3, minimum_digits: 5 }, + octal: NumericBaseConfig { group_length: 4, minimum_digits: 0 }, + } + } +} + +declare_oxc_lint!( + /// ### What it does + /// Enforces a convention of grouping digits using numeric separators. + /// + /// ### Why is this bad? + /// Long numbers can become really hard to read, so cutting it into groups of digits, + /// separated with a _, is important to keep your code clear. This rule also enforces + /// a proper usage of the numeric separator, by checking if the groups of digits are + /// of the correct size. + /// + /// + /// ### Example + /// ```javascript + /// const invalid = [ + /// 1_23_4444, + /// 1_234.56789, + /// 0xAB_C_D_EF, + /// 0b10_00_1111, + /// 0o1_0_44_21, + /// 1_294_28771_2n, + /// ]; + /// const valid = [ + /// 1_234_567, + /// 1_234.567_89, + /// 0xAB_CD_EF, + /// 0b1000_1111, + /// 0o10_4421, + /// 1_294_287_712n, + /// ]; + /// ``` + NumericSeparatorsStyle, + style +); + +impl Rule for NumericSeparatorsStyle { + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + match node.kind() { + AstKind::NumberLiteral(number) => { + if self.only_if_contains_separator && !number.raw.contains('_') { + return; + } + + let formatted = self.format_number(number); + + if formatted != number.raw { + ctx.diagnostic_with_fix(NumericSeparatorsStyleDiagnostic(number.span), || { + Fix::new(formatted, number.span) + }); + } + } + AstKind::BigintLiteral(number) => { + let raw = number.span.source_text(ctx.source_text()); + + if self.only_if_contains_separator && !raw.contains('_') { + return; + } + + let formatted = self.format_bigint(number, raw); + + if formatted.len() != number.span.size() as usize { + ctx.diagnostic_with_fix(NumericSeparatorsStyleDiagnostic(number.span), || { + Fix::new(formatted, number.span) + }); + } + } + _ => {} + }; + } + + fn from_configuration(value: serde_json::Value) -> Self { + let mut cfg = Self::default(); + + if let Some(config) = value.get(0) { + if let Some(config) = config.get("binary") { + cfg.binary.set_numeric_base_from_config(config); + } + if let Some(config) = config.get("hexadecimal") { + cfg.hexadecimal.set_numeric_base_from_config(config); + } + if let Some(config) = config.get("number") { + cfg.number.set_numeric_base_from_config(config); + } + if let Some(config) = config.get("octal") { + cfg.octal.set_numeric_base_from_config(config); + } + + if let Some(val) = + config.get("onlyIfContainsSeparator").and_then(serde_json::Value::as_bool) + { + cfg.only_if_contains_separator = val; + } + } + + cfg + } +} + +impl NumericSeparatorsStyle { + fn format_number(&self, number: &NumberLiteral) -> String { + use oxc_syntax::NumberBase; + + match number.base { + NumberBase::Binary => self.format_binary(number.raw), + NumberBase::Decimal | oxc_syntax::NumberBase::Float => self.format_decimal(number.raw), + NumberBase::Hex => self.format_hex(number.raw), + NumberBase::Octal => self.format_octal(number.raw), + } + } + + fn format_bigint(&self, number: &BigintLiteral, raw: &str) -> String { + use oxc_syntax::BigintBase; + + let raw_without_bigint_n_suffix = &raw[..raw.len() - 1]; + let mut formatted = match number.base { + BigintBase::Binary => self.format_binary(raw_without_bigint_n_suffix), + BigintBase::Decimal => self.format_decimal(raw_without_bigint_n_suffix), + BigintBase::Hex => self.format_hex(raw_without_bigint_n_suffix), + BigintBase::Octal => self.format_octal(raw_without_bigint_n_suffix), + }; + formatted.push('n'); + formatted + } + + fn format_binary(&self, raw_number: &str) -> String { + let prefix = &raw_number[0..2]; + + let mut to_format = raw_number[2..].replace('_', ""); + + add_separators(&mut to_format, &SeparatorDir::Right, &self.binary); + to_format.insert_str(0, prefix); + to_format + } + + fn format_hex(&self, number_raw: &str) -> String { + let prefix = &number_raw[0..2]; + + let mut to_format = number_raw[2..].replace('_', ""); + + add_separators(&mut to_format, &SeparatorDir::Right, &self.hexadecimal); + to_format.insert_str(0, prefix); + to_format + } + + fn format_octal(&self, number_raw: &str) -> String { + // Legacy octal numbers are 0-prefixed, e.g. `010 === 8`. + // Legacy octal notation does not support `_` prefixes. + let is_legacy = number_raw.as_bytes()[1] != b'o' && number_raw.as_bytes()[1] != b'O'; + if is_legacy { + return number_raw.to_string(); + } + + let prefix = &number_raw[0..2]; + + let mut to_format = number_raw[2..].replace('_', ""); + + add_separators(&mut to_format, &SeparatorDir::Right, &self.octal); + to_format.insert_str(0, prefix); + to_format + } + + fn format_decimal(&self, number_raw: &str) -> String { + lazy_static! { + static ref NUMBER_REGEX: Regex = + Regex::new(r"^([\d._]*?)(?:([Ee])([+-])?([\d_]+))?$").unwrap(); + } + + let caps = NUMBER_REGEX.captures(number_raw).unwrap(); + + let mut out = String::new(); + + { + let number = caps.get(1).unwrap().as_str().replace('_', ""); + + if let Some((whole, decimal)) = number.split_once('.') { + if !whole.is_empty() { + let mut s = whole.to_string(); + add_separators(&mut s, &SeparatorDir::Right, &self.number); + out.push_str(&s); + }; + + out.push('.'); + + if !decimal.is_empty() { + let mut s = decimal.to_string(); + add_separators(&mut s, &SeparatorDir::Left, &self.number); + out.push_str(&s); + } + } else { + out.push_str(number.as_str()); + add_separators(&mut out, &SeparatorDir::Right, &self.number); + } + } + + if let Some(mark) = caps.get(2) { + out.push_str(mark.as_str()); + } + if let Some(sign) = caps.get(3) { + out.push_str(sign.as_str()); + } + if let Some(power) = caps.get(4) { + let mut s = power.as_str().replace('_', ""); + add_separators(&mut s, &SeparatorDir::Right, &self.number); + out.push_str(&s); + } + + out + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct NumericBaseConfig { + group_length: usize, + minimum_digits: usize, +} +impl NumericBaseConfig { + pub(self) fn set_numeric_base_from_config(&mut self, val: &serde_json::Value) { + if let Some(group_length) = val.get("groupLength").and_then(serde_json::Value::as_u64) { + self.group_length = usize::try_from(group_length).unwrap(); + } + if let Some(minimum_digits) = val.get("minimumDigits").and_then(serde_json::Value::as_u64) { + self.minimum_digits = usize::try_from(minimum_digits).unwrap(); + } + } +} + +enum SeparatorDir { + Left, + Right, +} + +fn add_separators(s: &mut String, dir: &SeparatorDir, config: &NumericBaseConfig) { + if s.len() < config.minimum_digits || s.len() < config.group_length + 1 { + return; + } + + match dir { + SeparatorDir::Right => { + let mut pos = s.len(); + while pos > config.group_length { + pos -= config.group_length; + s.insert(pos, '_'); + } + } + SeparatorDir::Left => { + let mut pos = config.group_length; + while pos < s.len() { + s.insert(pos, '_'); + pos += config.group_length + 1; + } + } + } +} + +#[test] +fn test_with_snapshot() { + use crate::tester::Tester; + + let fail = vec![ + "const foo = 0b10_10_0001", + "const foo = 0b0_00_0", + "const foo = 0b10101010101010", + "const foo = 0B10101010101010", + "const foo = 0xA_B_CDE_F0", + "const foo = 0xABCDEF", + "const foo = 0xA_B", + "const foo = 0XAB_C_D", + "const foo = 0o12_34_5670", + "const foo = 0o7_7_77", + "const foo = 0o010101010101", + "const foo = 0O010101010101", + "const foo = 0b10_10_0001n", + "const foo = 0b0_00_0n", + "const foo = 0b10101010101010n", + "const foo = 0B10101010101010n", + "const foo = 1_9_223n", + "const foo = 80_7n", + "const foo = 123456789_100n", + "const foo = 1e10000", + "const foo = 39804e10000", + "const foo = -123456e100", + "const foo = -100000e-10000", + "const foo = -1000e+10000", + "const foo = -1000e+00010000", + "const foo = 3.6e12000", + "const foo = -1200000e5", + "const foo = 3.65432E12000", + "const foo = 9807.1234567", + "const foo = 3819.123_4325", + "const foo = 138789.12343_2_42", + "const foo = .000000_1", + "const foo = 12345678..toString()", + "const foo = 12345678 .toString()", + "const foo = .00000", + "const foo = 0.00000", + // Numbers + "const foo = 1_2_345_678", + "const foo = 12_3", + "const foo = 1234567890", + // Negative numbers + "const foo = -100000_1", + ]; + + Tester::new_without_config(NumericSeparatorsStyle::NAME, vec![], fail).test_and_snapshot(); +} + +#[test] +fn test_number_binary() { + use crate::tester::Tester; + + let pass = vec![ + "const foo = 0b1010_0001_1000_0101", + "const foo = 0b0000", + "const foo = 0b10", + "const foo = 0b1_0111_0101_0101", + "const foo = 0B1010", + ]; + + let fail = vec![ + "const foo = 0b10_10_0001", + "const foo = 0b0_00_0", + "const foo = 0b10101010101010", + "const foo = 0B10101010101010", + ]; + + let fix = vec![ + ("const foo = 0b10_10_0001", "const foo = 0b1010_0001", None), + ("const foo = 0b0_00_0", "const foo = 0b0000", None), + ("const foo = 0b10101010101010", "const foo = 0b10_1010_1010_1010", None), + ("const foo = 0B10101010101010", "const foo = 0B10_1010_1010_1010", None), + ]; + + Tester::new_without_config(NumericSeparatorsStyle::NAME, pass, fail).expect_fix(fix).test(); +} + +#[test] +fn test_number_hexadecimal() { + use crate::tester::Tester; + + let pass = vec![ + "const foo = 0xAB_CD", + "const foo = 0xAB", + "const foo = 0xA", + "const foo = 0xA_BC_DE_F0", + "const foo = 0xab_e8_12", + "const foo = 0xe", + "const foo = 0Xab_e3_cd", + ]; + + let fail = vec![ + "const foo = 0xA_B_CDE_F0", + "const foo = 0xABCDEF", + "const foo = 0xA_B", + "const foo = 0XAB_C_D", + ]; + + let fix = vec![("const foo = 0xA_B_CDE_F0", "const foo = 0xA_BC_DE_F0", None)]; + + Tester::new_without_config(NumericSeparatorsStyle::NAME, pass, fail).expect_fix(fix).test(); +} + +#[test] +fn test_number_octal() { + use crate::tester::Tester; + + let pass = vec![ + "const foo = 0o1234_5670", + "const foo = 0o7777", + "const foo = 0o01", + "const foo = 0o12_7000_0000", + "const foo = 0O1111_1111", + // Legacy + "const foo = 0777777", + "let foo = 0111222", + ]; + + let fail = vec![ + "const foo = 0o12_34_5670", + "const foo = 0o7_7_77", + "const foo = 0o010101010101", + "const foo = 0O010101010101", + ]; + + let fix = vec![("const foo = 0o12_34_5670", "const foo = 0o1234_5670", None)]; + + Tester::new_without_config(NumericSeparatorsStyle::NAME, pass, fail).expect_fix(fix).test(); +} + +#[test] +fn test_bigint_binary() { + use crate::tester::Tester; + + let pass = vec![ + "const foo = 0b1010_0001_1000_0101n", + "const foo = 0b0000n", + "const foo = 0b10n", + "const foo = 0b1_0111_0101_0101n", + "const foo = 0B1010n", + ]; + + let fail = vec![ + "const foo = 0b10_10_0001n", + "const foo = 0b0_00_0n", + "const foo = 0b10101010101010n", + "const foo = 0B10101010101010n", + ]; + + let fix = vec![ + ("const foo = 0b10_10_0001n", "const foo = 0b1010_0001n", None), + ("const foo = 0b0_00_0n", "const foo = 0b0000n", None), + ("const foo = 0b10101010101010n", "const foo = 0b10_1010_1010_1010n", None), + ("const foo = 0B10101010101010n", "const foo = 0B10_1010_1010_1010n", None), + ]; + + Tester::new_without_config(NumericSeparatorsStyle::NAME, pass, fail).expect_fix(fix).test(); +} + +#[test] +fn test_bigint() { + use crate::tester::Tester; + + let pass = vec![ + "const foo = 9_223_372_036_854_775_807n", + "const foo = 807n", + "const foo = 1n", + "const foo = 9_372_854_807n", + "const foo = 9807n", + "const foo = 0n", + ]; + + let fail = vec![ + // BigInt + "const foo = 1_9_223n", + "const foo = 80_7n", + "const foo = 123456789_100n", + ]; + + let fix = vec![("const foo = 1_9_223n", "const foo = 19_223n", None)]; + + Tester::new_without_config(NumericSeparatorsStyle::NAME, pass, fail).expect_fix(fix).test(); +} + +#[test] +fn test_number_decimal_exponential() { + use crate::tester::Tester; + + let pass = vec![ + "const foo = 1e10_000", + "const foo = 39_804e1000", + "const foo = -123_456e-100", + "const foo = -100_000e-100_000", + "const foo = -100_000e+100_000", + "const foo = 3.6e12_000", + "const foo = 3.6E12_000", + "const foo = -1_200_000e5", + ]; + + let fail = vec![ + "const foo = 1e10000", + "const foo = 39804e10000", + "const foo = -123456e100", + "const foo = -100000e-10000", + "const foo = -1000e+10000", + "const foo = -1000e+00010000", + "const foo = 3.6e12000", + "const foo = -1200000e5", + "const foo = 3.65432E12000", + ]; + + let fix = vec![ + ("const foo = 1e10000", "const foo = 1e10_000", None), + ("const foo = 39804e10000", "const foo = 39_804e10_000", None), + ("const foo = -123456e100", "const foo = -123_456e100", None), + ("const foo = -100000e-10000", "const foo = -100_000e-10_000", None), + ("const foo = -1000e+10000", "const foo = -1000e+10_000", None), + ("const foo = -1000e+00010000", "const foo = -1000e+00_010_000", None), + ("const foo = 3.6e12000", "const foo = 3.6e12_000", None), + ("const foo = -1200000e5", "const foo = -1_200_000e5", None), + ("const foo = 3.65432E12000", "const foo = 3.654_32E12_000", None), + ]; + + Tester::new_without_config(NumericSeparatorsStyle::NAME, pass, fail).expect_fix(fix).test(); +} + +#[test] +fn test_number_decimal_float() { + use crate::tester::Tester; + + let pass = vec![ + "const foo = 9807.123", + "const foo = 3819.123_432", + "const foo = 138_789.123_432_42", + "const foo = .000_000_1", + ]; + + let fail = vec![ + "const foo = 9807.1234567", + "const foo = 3819.123_4325", + "const foo = 138789.12343_2_42", + "const foo = .000000_1", + "const foo = 12345678..toString()", + "const foo = 12345678 .toString()", + "const foo = .00000", + "const foo = 0.00000", + ]; + + let fix = vec![("const foo = 9807.1234567", "const foo = 9807.123_456_7", None)]; + + Tester::new_without_config(NumericSeparatorsStyle::NAME, pass, fail).expect_fix(fix).test(); +} + +#[test] +fn test_number_decimal_integer() { + use crate::tester::Tester; + + let pass = vec![ + // Numbers + "const foo = 123_456", + "const foo = 12_345_678", + "const foo = 123", + "const foo = 1", + "const foo = 1234", + // Negative numbers + "const foo = -4000", + "const foo = -50_000", + "const foo = -600_000", + "const foo = -7_000_000", + "const foo = -80_000_000", + ]; + + let fail = vec![ + // Numbers + "const foo = 1_2_345_678", + "const foo = 12_3", + "const foo = 1234567890", + // Negative numbers + "const foo = -100000_1", + ]; + + let fix = vec![ + ("const foo = 1234567890", "const foo = 1_234_567_890", None), + ("const foo = -100000_1", "const foo = -1_000_001", None), + ]; + + Tester::new_without_config(NumericSeparatorsStyle::NAME, pass, fail).expect_fix(fix).test(); +} + +#[test] +fn test_with_config() { + use crate::tester::Tester; + use serde_json::json; + + let pass = vec![ + ("1234567890", Some(json!([{ "onlyIfContainsSeparator": true }]))), + ("0b11111111", Some(json!([{ "onlyIfContainsSeparator": true }]))), + ("0o77777777", Some(json!([{ "onlyIfContainsSeparator": true }]))), + ("0xffffffff", Some(json!([{ "onlyIfContainsSeparator": true }]))), + ("1234567890n", Some(json!([{ "onlyIfContainsSeparator": true }]))), + ("0b11111111n", Some(json!([{ "onlyIfContainsSeparator": true }]))), + ("0o77777777n", Some(json!([{ "onlyIfContainsSeparator": true }]))), + ("0xffffffffn", Some(json!([{ "onlyIfContainsSeparator": true }]))), + ("12_34_56", Some(json!([{ "number": { "groupLength": 2 } }]))), + ("12345", Some(json!([{ "number": { "minimumDigits": 10 } }]))), + ("0b1_1_1_1_1", Some(json!([{ "binary": { "groupLength": 1 } }]))), + ("0o7_7_7_7_7", Some(json!([{ "octal": { "groupLength": 1 } }]))), + ("0xf_f_f_f_f", Some(json!([{ "hexadecimal": { "groupLength": 1 } }]))), + ("12_34_56n", Some(json!([{ "number": { "groupLength": 2 } }]))), + ("12345n", Some(json!([{ "number": { "minimumDigits": 10 } }]))), + ("0b1_1_1_1_1n", Some(json!([{ "binary": { "groupLength": 1 } }]))), + ("0o7_7_7_7_7n", Some(json!([{ "octal": { "groupLength": 1 } }]))), + ("0xf_f_f_f_fn", Some(json!([{ "hexadecimal": { "groupLength": 1 } }]))), + ]; + + let fail = vec![]; + + Tester::new(NumericSeparatorsStyle::NAME, pass, fail).test(); +} + +#[test] +fn test_misc() { + use crate::tester::Tester; + + let pass = vec![ + "const foo = -282_932 - (1938 / 10_000) * .1 + 18.100_000_2", + "const foo = NaN", + "const foo = Infinity", + "const foo = -Infinity", + "const foo = '1234567n'", + ]; + + let fail = vec![]; + + Tester::new_without_config(NumericSeparatorsStyle::NAME, pass, fail).test(); +} + +#[cfg(test)] +mod internal_tests { + use serde_json::json; + + use super::*; + + #[test] + fn test_from_configuration() { + let config = json!([{ + "binary": {"groupLength": 2, "minimumDigits": 4}, + "hexadecimal": {"groupLength": 8, "minimumDigits": 16}, + "number": {"groupLength": 32, "minimumDigits": 64}, + "octal": {"groupLength": 128, "minimumDigits": 256}, + "onlyIfContainsSeparator": true + }]); + let rule = NumericSeparatorsStyle::from_configuration(config); + + assert_eq!(rule.binary.group_length, 2); + assert_eq!(rule.binary.minimum_digits, 4); + assert_eq!(rule.hexadecimal.group_length, 8); + assert_eq!(rule.hexadecimal.minimum_digits, 16); + assert_eq!(rule.number.group_length, 32); + assert_eq!(rule.number.minimum_digits, 64); + assert_eq!(rule.octal.group_length, 128); + assert_eq!(rule.octal.minimum_digits, 256); + assert!(rule.only_if_contains_separator); + } + + #[test] + fn test_from_empty_configuration() { + let rule = NumericSeparatorsStyle::from_configuration(json!([])); + assert_eq!(rule, NumericSeparatorsStyle::default()); + } +} diff --git a/crates/oxc_linter/src/snapshots/numeric_separators_style.snap b/crates/oxc_linter/src/snapshots/numeric_separators_style.snap new file mode 100644 index 000000000..1b2ed62e8 --- /dev/null +++ b/crates/oxc_linter/src/snapshots/numeric_separators_style.snap @@ -0,0 +1,285 @@ +--- +source: crates/oxc_linter/src/tester.rs +expression: numeric_separators_style +--- + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 0b10_10_0001 + · ──────────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 0b0_00_0 + · ──────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 0b10101010101010 + · ──────────────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 0B10101010101010 + · ──────────────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 0xA_B_CDE_F0 + · ──────────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 0xABCDEF + · ──────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 0xA_B + · ───── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 0XAB_C_D + · ──────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 0o12_34_5670 + · ──────────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 0o7_7_77 + · ──────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 0o010101010101 + · ────────────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 0O010101010101 + · ────────────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 0b10_10_0001n + · ───────────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 0b0_00_0n + · ───────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 0b10101010101010n + · ───────────────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 0B10101010101010n + · ───────────────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 1_9_223n + · ──────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 80_7n + · ───── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 123456789_100n + · ────────────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 1e10000 + · ─────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 39804e10000 + · ─────────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = -123456e100 + · ────────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = -100000e-10000 + · ───────────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = -1000e+10000 + · ─────────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = -1000e+00010000 + · ────────────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 3.6e12000 + · ───────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = -1200000e5 + · ───────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 3.65432E12000 + · ───────────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 9807.1234567 + · ──────────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 3819.123_4325 + · ───────────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 138789.12343_2_42 + · ───────────────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = .000000_1 + · ───────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 12345678..toString() + · ───────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 12345678 .toString() + · ──────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = .00000 + · ────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 0.00000 + · ─────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 1_2_345_678 + · ─────────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 12_3 + · ──── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = 1234567890 + · ────────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + + ⚠ eslint-plugin-unicorn(numeric-separators-style): Invalid group length in numeric value. + ╭─[numeric_separators_style.tsx:1:1] + 1 │ const foo = -100000_1 + · ──────── + ╰──── + help: Group digits with numeric separators (_) so longer numbers are easier to read. + +