Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve number-literal-case rule #490

Merged
merged 8 commits into from Dec 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 @@ -114,7 +114,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