Skip to content

Commit

Permalink
Improve number-literal-case rule (#490)
Browse files Browse the repository at this point in the history
  • Loading branch information
fisker authored and sindresorhus committed Dec 30, 2019
1 parent c4ffb1a commit cea1346
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 36 deletions.
38 changes: 31 additions & 7 deletions docs/rules/number-literal-case.md
@@ -1,26 +1,42 @@
# Enforce lowercase identifier and uppercase value for number literals
# Enforce proper case for numeric literals

Enforces a convention of defining number literals where the literal identifier is written in lowercase and the value in uppercase. Differentiating the casing of the identifier and value clearly separates them and makes your code more readable.
Differentiating the casing of the identifier and value clearly separates them and makes your code more readable.

This rule is fixable.
- Lowercase identifier and uppercase value for [`Number`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type) and [`BigInt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#BigInt_type).
- Lowercase `e` for exponential notation.

This rule is fixable.

## Fail

[Hexadecimal](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Hexadecimal)

```js
const foo = 0XFF;
const foo = 0xff;
const foo = 0Xff;
const foo = 0Xffn;
```

[Binary](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Binary)

```js
const foo = 0B11;
const foo = 0B10;
const foo = 0B10n;
```

[Octal](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Octal)

```js
const foo = 0O10;
const foo = 0O76;
const foo = 0O76n;
```

Exponential notation

```js
const foo = 2E-5;
```

## Pass

Expand All @@ -29,9 +45,17 @@ const foo = 0xFF;
```

```js
const foo = 0b11;
const foo = 0b10;
```

```js
const foo = 0o76;
```

```js
const foo = 0xFFn;
```

```js
const foo = 0o10;
const foo = 2e+5;
```
2 changes: 1 addition & 1 deletion readme.md
Expand Up @@ -113,7 +113,7 @@ Configure it in `package.json`.
- [no-unsafe-regex](docs/rules/no-unsafe-regex.md) - Disallow unsafe regular expressions.
- [no-unused-properties](docs/rules/no-unused-properties.md) - Disallow unused object properties.
- [no-zero-fractions](docs/rules/no-zero-fractions.md) - Disallow number literals with zero fractions or dangling dots. *(fixable)*
- [number-literal-case](docs/rules/number-literal-case.md) - Enforce lowercase identifier and uppercase value for number literals. *(fixable)*
- [number-literal-case](docs/rules/number-literal-case.md) - Enforce proper case for numeric literals. *(fixable)*
- [prefer-add-event-listener](docs/rules/prefer-add-event-listener.md) - Prefer `.addEventListener()` and `.removeEventListener()` over `on`-functions. *(partly fixable)*
- [prefer-dataset](docs/rules/prefer-dataset.md) - Prefer using `.dataset` on DOM elements over `.setAttribute(…)`. *(fixable)*
- [prefer-event-key](docs/rules/prefer-event-key.md) - Prefer `KeyboardEvent#key` over `KeyboardEvent#keyCode`. *(partly fixable)*
Expand Down
26 changes: 15 additions & 11 deletions rules/number-literal-case.js
@@ -1,28 +1,32 @@
'use strict';
const getDocumentationUrl = require('./utils/get-documentation-url');

const fix = value => {
if (!/^0[A-Za-z]/.test(value)) {
return value;
const fix = (value, isBigInt) => {
value = value.toLowerCase();
if (value.startsWith('0x')) {
value = '0x' + value.slice(2).toUpperCase();
}

const indicator = value[1].toLowerCase();
const newValue = value.slice(2).toUpperCase();

return `0${indicator}${newValue}`;
return `${value}${isBigInt ? 'n' : ''}`;
};

const create = context => {
return {
Literal: node => {
const value = node.raw;
const fixedValue = fix(value);
const {value, raw, bigint} = node;
const isBigInt = Boolean(bigint);

if (typeof value !== 'number' && !isBigInt) {
return;
}

const fixed = fix(isBigInt ? bigint : raw, isBigInt);

if (value !== fixedValue) {
if (raw !== fixed) {
context.report({
node,
message: 'Invalid number literal casing.',
fix: fixer => fixer.replaceText(node, fixedValue)
fix: fixer => fixer.replaceText(node, fixed)
});
}
}
Expand Down
78 changes: 61 additions & 17 deletions test/number-literal-case.js
Expand Up @@ -8,7 +8,7 @@ const ruleTester = avaRuleTester(test, {
es6: true
},
parserOptions: {
sourceType: 'module'
ecmaVersion: 2020
}
});

Expand All @@ -17,43 +17,87 @@ const error = {
message: 'Invalid number literal casing.'
};

// TODO: Add numeric separator tests when ESLint supports it.
ruleTester.run('number-literal-case', rule, {
valid: [
'const foo = 0xFF',
'const foo = 0b11',
'const foo = 0o10',
'const foo = \'0Xff\''
// Number
'const foo = 1234',
'const foo = 0777',
'const foo = 0888',
'const foo = 0b10',
'const foo = 0o1234567',
'const foo = 0xABCDEF',

// BigInt
'const foo = 1234n',
'const foo = 0b10n',
'const foo = 0o1234567n',
'const foo = 0xABCDEFn',

// Symbolic value
'const foo = NaN',
'const foo = +Infinity',
'const foo = -Infinity',

// Exponential notation
'const foo = 1.2e3',
'const foo = 1.2e-3',
'const foo = 1.2e+3',

// Not number
'const foo = \'0Xff\'',
'const foo = \'0Xffn\''
],
invalid: [
// Number
{
code: 'const foo = 0XFF',
code: 'const foo = 0B10',
errors: [error],
output: 'const foo = 0xFF'
output: 'const foo = 0b10'
},
{
code: 'const foo = 0xff',
code: 'const foo = 0O1234567',
errors: [error],
output: 'const foo = 0xFF'
output: 'const foo = 0o1234567'
},
{
code: 'const foo = 0Xff',
code: 'const foo = 0XaBcDeF',
errors: [error],
output: 'const foo = 0xABCDEF'
},

// BigInt
{
code: 'const foo = 0B10n',
errors: [error],
output: 'const foo = 0xFF'
output: 'const foo = 0b10n'
},
{
code: 'const foo = 0Xff',
code: 'const foo = 0O1234567n',
errors: [error],
output: 'const foo = 0o1234567n'
},
{
code: 'const foo = 0XaBcDeFn',
errors: [error],
output: 'const foo = 0xABCDEFn'
},

// Exponential notation
{
code: 'const foo = 1.2E3',
errors: [error],
output: 'const foo = 0xFF'
output: 'const foo = 1.2e3'
},
{
code: 'const foo = 0B11',
code: 'const foo = 1.2E-3',
errors: [error],
output: 'const foo = 0b11'
output: 'const foo = 1.2e-3'
},
{
code: 'const foo = 0O10',
code: 'const foo = 1.2E+3',
errors: [error],
output: 'const foo = 0o10'
output: 'const foo = 1.2e+3'
},
{
code: outdent`
Expand Down

0 comments on commit cea1346

Please sign in to comment.