diff --git a/docs/rules/number-literal-case.md b/docs/rules/number-literal-case.md new file mode 100644 index 0000000000..a1e699b1e2 --- /dev/null +++ b/docs/rules/number-literal-case.md @@ -0,0 +1,35 @@ +# Enforce lowercase identifier and uppercase value for number 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. + + +## Fail + +```js +const foo = 0XFF; +const foo = 0xff; +const foo = 0Xff; +``` + +```js +const foo = 0B11; +``` + +```js +const foo = 0O10; +``` + + +## Pass + +```js +const foo = 0xFF; +``` + +```js +const foo = 0b11; +``` + +```js +const foo = 0o10; +``` diff --git a/index.js b/index.js index 3e2a6444d3..7feaa39d83 100644 --- a/index.js +++ b/index.js @@ -18,7 +18,8 @@ module.exports = { 'unicorn/filename-case': ['error', {case: 'kebabCase'}], 'unicorn/no-abusive-eslint-disable': 'error', 'unicorn/no-process-exit': 'error', - 'unicorn/throw-new-error': 'error' + 'unicorn/throw-new-error': 'error', + 'unicorn/number-literal-case': 'error' } } } diff --git a/readme.md b/readme.md index 2ac9db3f24..f6850def18 100644 --- a/readme.md +++ b/readme.md @@ -38,7 +38,8 @@ Configure it in `package.json`. "unicorn/filename-case": ["error", {"case": "kebabCase"}], "unicorn/no-abusive-eslint-disable": "error", "unicorn/no-process-exit": "error", - "unicorn/throw-new-error": "error" + "unicorn/throw-new-error": "error", + "unicorn/number-literal-case": "error" } } } @@ -53,6 +54,7 @@ Configure it in `package.json`. - [no-abusive-eslint-disable](docs/rules/no-abusive-eslint-disable.md) - Enforce specifying rules to disable in `eslint-disable` comments. - [no-process-exit](docs/rules/no-process-exit.md) - Disallow `process.exit()`. - [throw-new-error](docs/rules/throw-new-error.md) - Require `new` when throwing an error. *(fixable)* +- [number-literal-case](docs/rules/number-literal-case.md) - Enforce lowercase identifier and uppercase value for number literals. *(fixable)* ## Recommended config diff --git a/rules/number-literal-case.js b/rules/number-literal-case.js new file mode 100644 index 0000000000..9a703be8f3 --- /dev/null +++ b/rules/number-literal-case.js @@ -0,0 +1,35 @@ +'use strict'; +const fix = value => { + if (!/^0[a-zA-Z]/.test(value)) { + return value; + } + + const indicator = value[1].toLowerCase(); + const val = value.slice(2).toUpperCase(); + + return `0${indicator}${val}`; +}; + +const create = context => { + return { + Literal: node => { + const value = node.raw; + const fixedValue = fix(value); + + if (value !== fixedValue) { + context.report({ + node, + message: 'Invalid number literal casing', + fix: fixer => fixer.replaceText(node, fixedValue) + }); + } + } + }; +}; + +module.exports = { + meta: { + fixable: 'code' + }, + create +}; diff --git a/test/number-literal-case.js b/test/number-literal-case.js new file mode 100644 index 0000000000..a1dca25d8c --- /dev/null +++ b/test/number-literal-case.js @@ -0,0 +1,75 @@ +import test from 'ava'; +import avaRuleTester from 'eslint-ava-rule-tester'; +import rule from '../rules/number-literal-case'; + +const ruleTester = avaRuleTester(test, { + env: { + es6: true + }, + parserOptions: { + sourceType: 'module' + } +}); + +const error = { + ruleId: 'number-literal-case', + message: 'Invalid number literal casing' +}; + +ruleTester.run('number-literal-case', rule, { + valid: [ + 'const foo = 0xFF', + 'const foo = 0b11', + 'const foo = 0o10', + `const foo = '0Xff'` + ], + invalid: [ + { + code: 'const foo = 0XFF', + errors: [error], + output: 'const foo = 0xFF' + }, + { + code: 'const foo = 0xff', + errors: [error], + output: 'const foo = 0xFF' + }, + { + code: 'const foo = 0Xff', + errors: [error], + output: 'const foo = 0xFF' + }, + { + code: 'const foo = 0Xff', + errors: [error], + output: 'const foo = 0xFF' + }, + { + code: 'const foo = 0B11', + errors: [error], + output: 'const foo = 0b11' + }, + { + code: 'const foo = 0O10', + errors: [error], + output: 'const foo = 0o10' + }, + { + code: ` + const foo = 255; + + if (foo === 0xff) { + console.log('invalid'); + } + `, + errors: [error], + output: ` + const foo = 255; + + if (foo === 0xFF) { + console.log('invalid'); + } + ` + } + ] +});