From b4b8585b1906eed44af48aff0e7f5e1d01fa0754 Mon Sep 17 00:00:00 2001 From: Rantetsu Inori Date: Tue, 18 Feb 2025 01:15:59 +0800 Subject: [PATCH 01/14] `escape-case`: Support lowercase --- docs/rules/escape-case.md | 28 ++- docs/rules/number-literal-case.md | 95 ++++++- rules/escape-case.js | 33 ++- test/escape-case.js | 395 ++++++++++++++++++++++++++---- 4 files changed, 492 insertions(+), 59 deletions(-) diff --git a/docs/rules/escape-case.md b/docs/rules/escape-case.md index 18b9782ebe..195a11d4a3 100644 --- a/docs/rules/escape-case.md +++ b/docs/rules/escape-case.md @@ -7,7 +7,7 @@ -Enforces defining escape sequence values with uppercase characters rather than lowercase ones. This promotes readability by making the escaped value more distinguishable from the identifier. +Enforces a consistent escaped value style by defining escape sequence values with uppercase or lowercase characters. The default style is uppercase, which promotes readability by making the escaped value more distinguishable from the identifier. ## Fail @@ -26,3 +26,29 @@ const foo = '\uD834'; const foo = '\u{1D306}'; const foo = '\cA'; ``` + +## Options + +Type: `string`\ +Default: `'uppercase'` + +- `'uppercase'` (default) + - Always use escape sequence values with uppercase characters. +- `'lowercase'` + - Always use escape sequence values with lowercase characters. + +```js +// eslint unicorn/escape-case: ["error", "lowercase"] + +// Fail +const foo = '\xA9'; +const foo = '\uD834'; +const foo = '\u{1D306}'; +const foo = '\cA'; + +// Pass +const foo = '\xa9'; +const foo = '\ud834'; +const foo = '\u{1d306}'; +const foo = '\ca'; +``` diff --git a/docs/rules/number-literal-case.md b/docs/rules/number-literal-case.md index dbb092416e..b64c7b9fb1 100644 --- a/docs/rules/number-literal-case.md +++ b/docs/rules/number-literal-case.md @@ -7,7 +7,7 @@ -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. The default style is: - 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. @@ -64,3 +64,96 @@ const foo = 0xFFn; ```js const foo = 2e+5; ``` + +## Options + +Type: `object` + +Default options: + +```js +{ + 'unicorn/number-literal-case': [ + 'error', + { + hexadecimalValue: true, + radixIdentifier: false, + exponentialNotation: false + } + ] +} +``` + +### hexadecimalValue + +Type: `'uppercase' | 'lowercase' | 'ignore'`\ +Default: `'uppercase'` + +Specify whether the hexadecimal number value (ABCDEF) should be in `uppercase`, `lowercase`, or `ignore` the check. Defaults to `'uppercase'`. + +Example: +```js +// eslint unicorn/number-literal-case: ["error", {"hexadecimalValue": "lowercase"}] + +// Fail +const foo = 0XFF; +const foo = 0xFF; +const foo = 0XFFn; +const foo = 0xFFn; + +// Pass +const foo = 0Xff; +const foo = 0xff; +const foo = 0Xffn; +const foo = 0xffn; +``` + +### radixIdentifier + +Type: `'uppercase' | 'lowercase' | 'ignore'`\ +Default: `'lowercase'` + +Specify whether the radix indentifer (`0x`, `0o`, `0b`) should be in `uppercase`, `lowercase`, or `ignore` the check. Defaults to `'lowercase'`. + +Example: +```js +// eslint unicorn/number-literal-case: ["error", {"radixIdentifier": "uppercase"}] + +// Fail +const foo = 0xFF; +const foo = 0o76; +const foo = 0b10; +const foo = 0xFFn; +const foo = 0o76n; +const foo = 0b10n; + +// Pass +const foo = 0XFF; +const foo = 0O76; +const foo = 0B10; +const foo = 0XFFn; +const foo = 0O76n; +const foo = 0B10n; +``` + +### exponentialNotation + +Type: `'uppercase' | 'lowercase' | 'ignore'`\ +Default: `'lowercase'` + +Specify whether the exponential notation (`e`) should be in `uppercase`, `lowercase`, or `ignore` the check. Defaults to `'lowercase'`. + +Example: +```js +// eslint unicorn/number-literal-case: ["error", {"exponentialNotation": "uppercase"}] + +// Fail +const foo = 2e-5; +const foo = 2e+5; +const foo = 2e99; + +// Pass +const foo = 2E-5; +const foo = 2E+5; +const foo = 2E99; +``` diff --git a/rules/escape-case.js b/rules/escape-case.js index 72b7c4d8f9..90c1a5e120 100644 --- a/rules/escape-case.js +++ b/rules/escape-case.js @@ -1,20 +1,22 @@ import {replaceTemplateElement} from './fix/index.js'; import {isRegexLiteral, isStringLiteral, isTaggedTemplateLiteral} from './ast/index.js'; -const MESSAGE_ID = 'escape-case'; +const MESSAGE_ID_UPPERCASE = 'escape-uppercase'; +const MESSAGE_ID_LOWERCASE = 'escape-lowercase'; const messages = { - [MESSAGE_ID]: 'Use uppercase characters for the value of the escape sequence.', + [MESSAGE_ID_UPPERCASE]: 'Use uppercase characters for the value of the escape sequence.', + [MESSAGE_ID_LOWERCASE]: 'Use lowercase characters for the value of the escape sequence.', }; -const escapeWithLowercase = /(?<=(?:^|[^\\])(?:\\\\)*\\)(?x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|u{[\dA-Fa-f]+})/g; -const escapePatternWithLowercase = /(?<=(?:^|[^\\])(?:\\\\)*\\)(?x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|u{[\dA-Fa-f]+}|c[a-z])/g; -const getProblem = ({node, original, regex = escapeWithLowercase, fix}) => { - const fixed = original.replace(regex, data => data.slice(0, 1) + data.slice(1).toUpperCase()); +const escapeCase = /(?<=(?:^|[^\\])(?:\\\\)*\\)(?x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|u{[\dA-Fa-f]+})/g; +const escapePatternCase = /(?<=(?:^|[^\\])(?:\\\\)*\\)(?x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|u{[\dA-Fa-f]+}|c[A-Za-z])/g; +const getProblem = ({node, original, regex = escapeCase, lowercase, fix}) => { + const fixed = original.replace(regex, data => data[0] + data.slice(1)[lowercase ? 'toLowerCase' : 'toUpperCase']()); if (fixed !== original) { return { node, - messageId: MESSAGE_ID, + messageId: lowercase ? MESSAGE_ID_LOWERCASE : MESSAGE_ID_UPPERCASE, fix: fixer => fix ? fix(fixer, fixed) : fixer.replaceText(node, fixed), }; } @@ -22,11 +24,14 @@ const getProblem = ({node, original, regex = escapeWithLowercase, fix}) => { /** @param {import('eslint').Rule.RuleContext} context */ const create = context => { + const lowercase = context.options[0] === 'lowercase'; + context.on('Literal', node => { if (isStringLiteral(node)) { return getProblem({ node, original: node.raw, + lowercase, }); } }); @@ -36,7 +41,8 @@ const create = context => { return getProblem({ node, original: node.raw, - regex: escapePatternWithLowercase, + regex: escapePatternCase, + lowercase, }); } }); @@ -49,21 +55,30 @@ const create = context => { return getProblem({ node, original: node.value.raw, + lowercase, fix: (fixer, fixed) => replaceTemplateElement(fixer, node, fixed), }); }); }; +const schema = [ + { + enum: ['uppercase', 'lowercase'], + }, +]; + /** @type {import('eslint').Rule.RuleModule} */ const config = { create, meta: { type: 'suggestion', docs: { - description: 'Require escape sequences to use uppercase values.', + description: 'Require escape sequences to use uppercase or lowercase values.', recommended: true, }, fixable: 'code', + schema, + defaultOptions: ['uppercase'], messages, }, }; diff --git a/test/escape-case.js b/test/escape-case.js index 22e8f4ef96..2d205c018f 100644 --- a/test/escape-case.js +++ b/test/escape-case.js @@ -3,12 +3,16 @@ import {getTester} from './utils/test.js'; const {test} = getTester(import.meta); -const errors = [ - { - messageId: 'escape-case', - }, -]; +const uppercaseTest = { + errors: [{messageId: 'escape-uppercase'}], +}; +const lowercaseTest = { + options: ['lowercase'], + errors: [{messageId: 'escape-lowercase'}], +}; + +// 'uppercase' test({ valid: [ // Literal string @@ -66,239 +70,534 @@ test({ // Literal string { code: String.raw`const foo = "\xa9";`, - errors, output: String.raw`const foo = "\xA9";`, + ...uppercaseTest, }, // Mixed cases { code: String.raw`const foo = "\xAa";`, - errors, output: String.raw`const foo = "\xAA";`, + ...uppercaseTest, }, { code: String.raw`const foo = "\uAaAa";`, - errors, output: String.raw`const foo = "\uAAAA";`, + ...uppercaseTest, }, { code: String.raw`const foo = "\u{AaAa}";`, - errors, output: String.raw`const foo = "\u{AAAA}";`, + ...uppercaseTest, }, // Many { code: String.raw`const foo = "\xAab\xaab\xAAb\uAaAab\uaaaab\uAAAAb\u{AaAa}b\u{aaaa}b\u{AAAA}";`, - errors, output: String.raw`const foo = "\xAAb\xAAb\xAAb\uAAAAb\uAAAAb\uAAAAb\u{AAAA}b\u{AAAA}b\u{AAAA}";`, + ...uppercaseTest, }, { code: String.raw`const foo = "\ud834";`, - errors, output: String.raw`const foo = "\uD834";`, + ...uppercaseTest, }, { code: String.raw`const foo = "\u{1d306}";`, - errors, output: String.raw`const foo = "\u{1D306}";`, + ...uppercaseTest, }, { code: String.raw`const foo = "\ud834foo";`, - errors, output: String.raw`const foo = "\uD834foo";`, + ...uppercaseTest, }, { code: String.raw`const foo = "foo\ud834";`, - errors, output: String.raw`const foo = "foo\uD834";`, + ...uppercaseTest, }, { code: String.raw`const foo = "foo \ud834";`, - errors, output: String.raw`const foo = "foo \uD834";`, + ...uppercaseTest, }, { code: String.raw`const foo = "\\\ud834foo";`, - errors, output: String.raw`const foo = "\\\uD834foo";`, + ...uppercaseTest, }, { code: String.raw`const foo = "foo\\\ud834";`, - errors, output: String.raw`const foo = "foo\\\uD834";`, + ...uppercaseTest, }, { code: String.raw`const foo = "foo \\\ud834";`, - errors, output: String.raw`const foo = "foo \\\uD834";`, + ...uppercaseTest, }, // TemplateLiteral { code: 'const foo = `\\xa9`;', - errors, output: 'const foo = `\\xA9`;', + ...uppercaseTest, }, { code: 'const foo = `\\ud834`;', - errors, output: 'const foo = `\\uD834`;', + ...uppercaseTest, }, { code: 'const foo = `\\u{1d306}`;', - errors, output: 'const foo = `\\u{1D306}`;', + ...uppercaseTest, }, { code: 'const foo = `\\ud834foo`;', - errors, output: 'const foo = `\\uD834foo`;', + ...uppercaseTest, }, { code: 'const foo = `foo\\ud834`;', - errors, output: 'const foo = `foo\\uD834`;', + ...uppercaseTest, }, { code: 'const foo = `foo \\ud834`;', - errors, output: 'const foo = `foo \\uD834`;', + ...uppercaseTest, }, { code: 'const foo = `${"\ud834 foo"} \\ud834`;', - errors, output: 'const foo = `${"\uD834 foo"} \\uD834`;', + ...uppercaseTest, }, { code: 'const foo = `\\ud834${foo}\\ud834${foo}\\ud834`;', - errors: Array.from({length: 3}, () => errors[0]), output: 'const foo = `\\uD834${foo}\\uD834${foo}\\uD834`;', + ...uppercaseTest, + errors: Array.from({length: 3}, () => uppercaseTest.errors[0]), }, { code: 'const foo = `\\\\\\ud834foo`;', - errors, output: 'const foo = `\\\\\\uD834foo`;', + ...uppercaseTest, }, { code: 'const foo = `foo\\\\\\ud834`;', - errors, output: 'const foo = `foo\\\\\\uD834`;', + ...uppercaseTest, }, { code: 'const foo = `foo \\\\\\ud834`;', - errors, output: 'const foo = `foo \\\\\\uD834`;', + ...uppercaseTest, }, // TODO: This is not safe, it will be broken if `tagged` uses `arguments[0].raw` // #2341 { code: 'const foo = tagged`\\uAaAa`;', - errors, output: 'const foo = tagged`\\uAAAA`;', + ...uppercaseTest, }, { code: 'const foo = `\\uAaAa```;', - errors, output: 'const foo = `\\uAAAA```;', + ...uppercaseTest, }, // Mixed cases { code: 'const foo = `\\xAa`;', - errors, output: 'const foo = `\\xAA`;', + ...uppercaseTest, }, { code: 'const foo = `\\uAaAa`;', - errors, output: 'const foo = `\\uAAAA`;', + ...uppercaseTest, }, { code: 'const foo = `\\u{AaAa}`;', - errors, output: 'const foo = `\\u{AAAA}`;', + ...uppercaseTest, }, // Many { code: 'const foo = `\\xAab\\xaab\\xAA${foo}\\uAaAab\\uaaaab\\uAAAAb\\u{AaAa}${foo}\\u{aaaa}b\\u{AAAA}`;', - errors: Array.from({length: 3}, () => errors[0]), output: 'const foo = `\\xAAb\\xAAb\\xAA${foo}\\uAAAAb\\uAAAAb\\uAAAAb\\u{AAAA}${foo}\\u{AAAA}b\\u{AAAA}`;', + ...uppercaseTest, + errors: Array.from({length: 3}, () => uppercaseTest.errors[0]), }, // Literal regex { code: String.raw`const foo = /\xa9/;`, - errors, output: String.raw`const foo = /\xA9/;`, + ...uppercaseTest, }, { code: String.raw`const foo = /\ud834/`, - errors, output: String.raw`const foo = /\uD834/`, + ...uppercaseTest, }, { code: String.raw`const foo = /\u{1d306}/u`, - errors, output: String.raw`const foo = /\u{1D306}/u`, + ...uppercaseTest, }, { code: String.raw`const foo = /\ca/`, - errors, output: String.raw`const foo = /\cA/`, + ...uppercaseTest, }, { code: String.raw`const foo = /foo\\\xa9/;`, - errors, output: String.raw`const foo = /foo\\\xA9/;`, + ...uppercaseTest, }, { code: String.raw`const foo = /foo\\\\\xa9/;`, - errors, output: String.raw`const foo = /foo\\\\\xA9/;`, + ...uppercaseTest, }, // Mixed cases { code: String.raw`const foo = /\xAa/;`, - errors, output: String.raw`const foo = /\xAA/;`, + ...uppercaseTest, }, { code: String.raw`const foo = /\uAaAa/;`, - errors, output: String.raw`const foo = /\uAAAA/;`, + ...uppercaseTest, }, { code: String.raw`const foo = /\u{AaAa}/;`, - errors, output: String.raw`const foo = /\u{AAAA}/;`, + ...uppercaseTest, }, // Many { code: String.raw`const foo = /\xAab\xaab\xAAb\uAaAab\uaaaab\uAAAAb\u{AaAa}b\u{aaaa}b\u{AAAA}b\ca/;`, - errors, output: String.raw`const foo = /\xAAb\xAAb\xAAb\uAAAAb\uAAAAb\uAAAAb\u{AAAA}b\u{AAAA}b\u{AAAA}b\cA/;`, + ...uppercaseTest, }, // RegExp { code: String.raw`const foo = new RegExp("/\xa9")`, - errors, output: String.raw`const foo = new RegExp("/\xA9")`, + ...uppercaseTest, }, { code: String.raw`const foo = new RegExp("/\ud834/")`, - errors, output: String.raw`const foo = new RegExp("/\uD834/")`, + ...uppercaseTest, }, { code: String.raw`const foo = new RegExp("/\u{1d306}/", "u")`, - errors, output: String.raw`const foo = new RegExp("/\u{1D306}/", "u")`, + ...uppercaseTest, + }, + ], +}); + +// 'lowercase' +test({ + valid: [ + // Literal string + String.raw`const foo = "\xa9";`, + String.raw`const foo = "\ud834";`, + String.raw`const foo = "\u{1d306}";`, + String.raw`const foo = "\ud834foo";`, + String.raw`const foo = "foo\ud834";`, + String.raw`const foo = "foo \ud834";`, + String.raw`const foo = "foo\\xBAR";`, + String.raw`const foo = "foo\\uBARBAZ";`, + String.raw`const foo = "foo\\\\xBAR";`, + String.raw`const foo = "foo\\\\uBARBAZ";`, + String.raw`const foo = "\cA";`, + + // TemplateLiteral + 'const foo = `\\xa9`;', + 'const foo = `\\ud834`;', + 'const foo = `\\u{1d306}`;', + 'const foo = `\\ud834FOO`;', + 'const foo = `foo\\ud834`;', + 'const foo = `foo \\ud834`;', + 'const foo = `${"\ud834 foo"} \\ud834`;', + 'const foo = `foo\\\\xBAR`;', + 'const foo = `foo\\\\uBARBAZ`;', + 'const foo = `foo\\\\\\\\xBAR`;', + 'const foo = `foo\\\\\\\\uBARBAZ`;', + 'const foo = `\\ca`;', + 'const foo = String.raw`\\uAaAa`;', + + // Literal regex + String.raw`const foo = /foo\xa9/`, + String.raw`const foo = /foo\ud834/`, + String.raw`const foo = /foo\u{1d306}/u`, + String.raw`const foo = /foo\ca/`, + // Escape + String.raw`const foo = /foo\\xA9/;`, + String.raw`const foo = /foo\\\\xA9/;`, + String.raw`const foo = /foo\\ud834/`, + String.raw`const foo = /foo\\u{1}/u`, + String.raw`const foo = /foo\\ca/`, + + // RegExp + String.raw`const foo = new RegExp("/\xa9")`, + String.raw`const foo = new RegExp("/\ud834/")`, + String.raw`const foo = new RegExp("/\u{1d306}/", "u")`, + String.raw`const foo = new RegExp("/\ca/")`, + String.raw`const foo = new RegExp("/\cA/")`, + ].map(code => ({code, options: lowercaseTest.options})), + invalid: [ + // Literal string + { + code: String.raw`const foo = "\xA9";`, + output: String.raw`const foo = "\xa9";`, + ...lowercaseTest, + }, + + // Mixed cases + { + code: String.raw`const foo = "\xaA";`, + output: String.raw`const foo = "\xaa";`, + ...lowercaseTest, + }, + { + code: String.raw`const foo = "\uaAaA";`, + output: String.raw`const foo = "\uaaaa";`, + ...lowercaseTest, + }, + { + code: String.raw`const foo = "\u{aAaA}";`, + output: String.raw`const foo = "\u{aaaa}";`, + ...lowercaseTest, + }, + + // Many + { + code: String.raw`const foo = "\xAab\xaAb\xaab\xAAb\uAaAab\uaaaab\uAAAAb\u{AaAa}b\u{aaaa}b\u{AAAA}";`, + output: String.raw`const foo = "\xaab\xaab\xaab\xaab\uaaaab\uaaaab\uaaaab\u{aaaa}b\u{aaaa}b\u{aaaa}";`, + ...lowercaseTest, + }, + + { + code: String.raw`const foo = "\uD834";`, + output: String.raw`const foo = "\ud834";`, + ...lowercaseTest, + }, + { + code: String.raw`const foo = "\u{1D306}";`, + output: String.raw`const foo = "\u{1d306}";`, + ...lowercaseTest, + }, + { + code: String.raw`const foo = "\uD834FOO";`, + output: String.raw`const foo = "\ud834FOO";`, + ...lowercaseTest, + }, + { + code: String.raw`const foo = "FOO\uD834";`, + output: String.raw`const foo = "FOO\ud834";`, + ...lowercaseTest, + }, + { + code: String.raw`const foo = "FOO \uD834";`, + output: String.raw`const foo = "FOO \ud834";`, + ...lowercaseTest, + }, + { + code: String.raw`const foo = "\\\uD834FOO";`, + output: String.raw`const foo = "\\\ud834FOO";`, + ...lowercaseTest, + }, + { + code: String.raw`const foo = "FOO\\\uD834";`, + output: String.raw`const foo = "FOO\\\ud834";`, + ...lowercaseTest, + }, + { + code: String.raw`const foo = "FOO \\\uD834";`, + output: String.raw`const foo = "FOO \\\ud834";`, + ...lowercaseTest, + }, + + // TemplateLiteral + { + code: 'const foo = `\\xA9`;', + output: 'const foo = `\\xa9`;', + ...lowercaseTest, + }, + { + code: 'const foo = `\\uD834`;', + output: 'const foo = `\\ud834`;', + ...lowercaseTest, + }, + { + code: 'const foo = `\\u{1D306}`;', + output: 'const foo = `\\u{1d306}`;', + ...lowercaseTest, + }, + { + code: 'const foo = `\\uD834FOO`;', + output: 'const foo = `\\ud834FOO`;', + ...lowercaseTest, + }, + { + code: 'const foo = `FOO\\uD834`;', + output: 'const foo = `FOO\\ud834`;', + ...lowercaseTest, + }, + { + code: 'const foo = `FOO \\uD834`;', + output: 'const foo = `FOO \\ud834`;', + ...lowercaseTest, + }, + { + code: 'const foo = `${"\uD834 FOO"} \\uD834`;', + output: 'const foo = `${"\ud834 FOO"} \\ud834`;', + ...lowercaseTest, + }, + { + code: 'const foo = `\\uD834${FOO}\\uD834${FOO}\\uD834`;', + output: 'const foo = `\\ud834${FOO}\\ud834${FOO}\\ud834`;', + ...lowercaseTest, + errors: Array.from({length: 3}, () => lowercaseTest.errors[0]), + }, + { + code: 'const foo = `\\\\\\uD834FOO`;', + output: 'const foo = `\\\\\\ud834FOO`;', + ...lowercaseTest, + }, + { + code: 'const foo = `FOO\\\\\\uD834`;', + output: 'const foo = `FOO\\\\\\ud834`;', + ...lowercaseTest, + }, + { + code: 'const foo = `FOO \\\\\\uD834`;', + output: 'const foo = `FOO \\\\\\ud834`;', + ...lowercaseTest, + }, + // TODO: This is not safe, it will be broken if `tagged` uses `arguments[0].raw` + // #2341 + { + code: 'const foo = tagged`\\uaAaA`;', + output: 'const foo = tagged`\\uaaaa`;', + ...lowercaseTest, + }, + { + code: 'const foo = `\\uaAaA```;', + output: 'const foo = `\\uaaaa```;', + ...lowercaseTest, + }, + + // Mixed cases + { + code: 'const foo = `\\xaA`;', + output: 'const foo = `\\xaa`;', + ...lowercaseTest, + }, + { + code: 'const foo = `\\uaAaA`;', + output: 'const foo = `\\uaaaa`;', + ...lowercaseTest, + }, + { + code: 'const foo = `\\u{aAaA}`;', + output: 'const foo = `\\u{aaaa}`;', + ...lowercaseTest, + }, + + // Many + { + code: 'const foo = `\\xAab\\xaab\\xaAb\\xAA${foo}\\uAaAab\\uaaaab\\uAAAAb\\u{AaAa}${foo}\\u{aaaa}b\\u{AAAA}`;', + output: 'const foo = `\\xaab\\xaab\\xaab\\xaa${foo}\\uaaaab\\uaaaab\\uaaaab\\u{aaaa}${foo}\\u{aaaa}b\\u{aaaa}`;', + ...lowercaseTest, + errors: Array.from({length: 3}, () => lowercaseTest.errors[0]), + }, + + // Literal regex + { + code: String.raw`const foo = /\xA9/;`, + output: String.raw`const foo = /\xa9/;`, + ...lowercaseTest, + }, + { + code: String.raw`const foo = /\uD834/`, + output: String.raw`const foo = /\ud834/`, + ...lowercaseTest, + }, + { + code: String.raw`const foo = /\u{1D306}/u`, + output: String.raw`const foo = /\u{1d306}/u`, + ...lowercaseTest, + }, + { + code: String.raw`const foo = /\cA/`, + output: String.raw`const foo = /\ca/`, + ...lowercaseTest, + }, + { + code: String.raw`const foo = /FOO\\\xA9/;`, + output: String.raw`const foo = /FOO\\\xa9/;`, + ...lowercaseTest, + }, + { + code: String.raw`const foo = /FOO\\\\\xA9/;`, + output: String.raw`const foo = /FOO\\\\\xa9/;`, + ...lowercaseTest, + }, + + // Mixed cases + { + code: String.raw`const foo = /\xaA/;`, + output: String.raw`const foo = /\xaa/;`, + ...lowercaseTest, + }, + { + code: String.raw`const foo = /\uaAaA/;`, + output: String.raw`const foo = /\uaaaa/;`, + ...lowercaseTest, + }, + { + code: String.raw`const foo = /\u{aAaA}/;`, + output: String.raw`const foo = /\u{aaaa}/;`, + ...lowercaseTest, + }, + + // Many + { + code: String.raw`const foo = /\xAab\xaAb\xaab\xAAb\uAaAab\uaaaab\uAAAAb\u{AaAa}b\u{aaaa}b\u{AAAA}b\cA/;`, + output: String.raw`const foo = /\xaab\xaab\xaab\xaab\uaaaab\uaaaab\uaaaab\u{aaaa}b\u{aaaa}b\u{aaaa}b\ca/;`, + ...lowercaseTest, + }, + + // RegExp + { + code: String.raw`const foo = new RegExp("/\xA9")`, + output: String.raw`const foo = new RegExp("/\xa9")`, + ...lowercaseTest, + }, + { + code: String.raw`const foo = new RegExp("/\uD834/")`, + output: String.raw`const foo = new RegExp("/\ud834/")`, + ...lowercaseTest, + }, + { + code: String.raw`const foo = new RegExp("/\u{1D306}/", "u")`, + output: String.raw`const foo = new RegExp("/\u{1d306}/", "u")`, + ...lowercaseTest, }, ], }); From 9a432b6f66d5aa6c123c35152946f9d6cdb7df7f Mon Sep 17 00:00:00 2001 From: Rantetsu Inori Date: Tue, 18 Feb 2025 02:41:30 +0800 Subject: [PATCH 02/14] `number-literal-case`: Support different cases --- docs/rules/number-literal-case.md | 6 +- rules/number-literal-case.js | 79 ++++++++++++++++++++-- test/number-literal-case.js | 107 +++++++++++++++++++++++++----- 3 files changed, 167 insertions(+), 25 deletions(-) diff --git a/docs/rules/number-literal-case.md b/docs/rules/number-literal-case.md index b64c7b9fb1..d3284df31e 100644 --- a/docs/rules/number-literal-case.md +++ b/docs/rules/number-literal-case.md @@ -76,9 +76,9 @@ Default options: 'unicorn/number-literal-case': [ 'error', { - hexadecimalValue: true, - radixIdentifier: false, - exponentialNotation: false + hexadecimalValue: 'uppercase', + radixIdentifier: 'lowercase', + exponentialNotation: 'lowercase' } ] } diff --git a/rules/number-literal-case.js b/rules/number-literal-case.js index da71d35759..be85c4ad87 100644 --- a/rules/number-literal-case.js +++ b/rules/number-literal-case.js @@ -6,25 +6,62 @@ const messages = { [MESSAGE_ID]: 'Invalid number literal casing.', }; -const fix = raw => { - let fixed = raw.toLowerCase(); - if (fixed.startsWith('0x')) { - fixed = '0x' + fixed.slice(2).toUpperCase(); +/** + @param {string} raw + @param {Options[keyof Options]} option + */ +const convertCase = (raw, option) => { + if (option === 'uppercase') { + return raw.toUpperCase(); + } + + if (option === 'lowercase') { + return raw.toLowerCase(); + } + + return raw; +}; + +/** + @param {string} raw + @param {Options} options + */ +const fix = (raw, options) => { + let fixed = raw; + let isSpecialBase = false; // Indicates that the number is hexadecimal, octal, or binary. + fixed = fixed.replace(/^(0[xob])(.*)/i, (_, radix, value) => { + isSpecialBase = true; + radix = convertCase(radix, options.radixIdentifier); + if (radix.toLowerCase() === '0x') { + value = convertCase(value, options.hexadecimalValue); + } + + return radix + value; + }); + + if (!isSpecialBase) { + fixed = fixed.replaceAll(/e/ig, expo => convertCase(expo, options.exponentialNotation)); } return fixed; }; /** @param {import('eslint').Rule.RuleContext} context */ -const create = () => ({ +const create = context => ({ Literal(node) { const {raw} = node; + /** @type {Options} */ + const options = context.options[0] ?? {}; + options.hexadecimalValue ??= 'uppercase'; + options.radixIdentifier ??= 'lowercase'; + options.exponentialNotation ??= 'lowercase'; + let fixed = raw; if (isNumberLiteral(node)) { - fixed = fix(raw); + fixed = fix(raw, options); } else if (isBigIntLiteral(node)) { - fixed = fix(raw.slice(0, -1)) + 'n'; + fixed = fix(raw.slice(0, -1), options) + 'n'; } if (raw !== fixed) { @@ -37,6 +74,28 @@ const create = () => ({ }, }); +/** @typedef {Record} Options */ + +const caseEnum = /** @type {const} */ ({ + enum: [ + 'uppercase', + 'lowercase', + 'ignore', + ], +}); + +const schema = [ + { + type: 'object', + additionalProperties: false, + properties: { + hexadecimalValue: caseEnum, + radixIdentifier: caseEnum, + exponentialNotation: caseEnum, + }, + }, +]; + /** @type {import('eslint').Rule.RuleModule} */ const config = { create: checkVueTemplate(create), @@ -47,6 +106,12 @@ const config = { recommended: true, }, fixable: 'code', + schema, + defaultOptions: [{ + hexadecimalValue: 'uppercase', + radixIdentifier: 'lowercase', + exponentialNotation: 'lowercase', + }], messages, }, }; diff --git a/test/number-literal-case.js b/test/number-literal-case.js index e43a8a4d0b..9008251e21 100644 --- a/test/number-literal-case.js +++ b/test/number-literal-case.js @@ -63,72 +63,66 @@ const tests = { 'const foo = 0b10_10n', 'const foo = 0o1_234_567n', 'const foo = 0xDEED_BEEFn', + + // Negative number + 'const foo = -1234', + 'const foo = -0b10', + 'const foo = -0o1234567', + 'const foo = -0xABCDEF', ], invalid: [ // Number { code: 'const foo = 0B10', - errors: [error], output: 'const foo = 0b10', }, { code: 'const foo = 0O1234567', - errors: [error], output: 'const foo = 0o1234567', }, { code: 'const foo = 0XaBcDeF', - errors: [error], output: 'const foo = 0xABCDEF', }, // BigInt { code: 'const foo = 0B10n', - errors: [error], output: 'const foo = 0b10n', }, { code: 'const foo = 0O1234567n', - errors: [error], output: 'const foo = 0o1234567n', }, { code: 'const foo = 0XaBcDeFn', - errors: [error], output: 'const foo = 0xABCDEFn', }, // `0n` { code: 'const foo = 0B0n', - errors: [error], output: 'const foo = 0b0n', }, { code: 'const foo = 0O0n', - errors: [error], output: 'const foo = 0o0n', }, { code: 'const foo = 0X0n', - errors: [error], output: 'const foo = 0x0n', }, // Exponential notation { code: 'const foo = 1.2E3', - errors: [error], output: 'const foo = 1.2e3', }, { code: 'const foo = 1.2E-3', - errors: [error], output: 'const foo = 1.2e-3', }, { code: 'const foo = 1.2E+3', - errors: [error], output: 'const foo = 1.2e+3', }, { @@ -139,7 +133,6 @@ const tests = { console.log('invalid'); } `, - errors: [error], output: outdent` const foo = 255; @@ -152,10 +145,94 @@ const tests = { // Numeric separator { code: 'const foo = 0XdeEd_Beefn', - errors: [error], output: 'const foo = 0xDEED_BEEFn', }, - ], + + // Negative number + { + code: 'const foo = -0B10', + output: 'const foo = -0b10', + }, + { + code: 'const foo = -0O1234567', + output: 'const foo = -0o1234567', + }, + { + code: 'const foo = -0XaBcDeF', + output: 'const foo = -0xABCDEF', + }, + + // Lowercase hexadecimal number value + ...[ + { + code: 'const foo = 0XaBcDeF', + output: 'const foo = 0xabcdef', + }, + { + code: 'const foo = 0XaBcDeFn', + output: 'const foo = 0xabcdefn', + }, + ].map(item => ({...item, options: [{hexadecimalValue: 'lowercase'}]})), + + // Uppercase radix indentifer + ...[ + { + code: 'const foo = 0b10', + output: 'const foo = 0B10', + }, + { + code: 'const foo = 0o1234567', + output: 'const foo = 0O1234567', + }, + { + code: 'const foo = 0xaBcDeF', + output: 'const foo = 0XABCDEF', + }, + ].map(item => ({...item, options: [{radixIdentifier: 'uppercase'}]})), + + // Uppercase exponential notation + ...[ + { + code: 'const foo = 1.2e3', + output: 'const foo = 1.2E3', + }, + { + code: 'const foo = 1.2e-3', + output: 'const foo = 1.2E-3', + }, + { + code: 'const foo = 1.2e+3', + output: 'const foo = 1.2E+3', + }, + ].map(item => ({...item, options: [{exponentialNotation: 'uppercase'}]})), + + // Mixed options + { + code: 'const foo = 0xaBcDeF', + output: 'const foo = 0Xabcdef', + options: [{hexadecimalValue: 'lowercase', radixIdentifier: 'uppercase'}], + }, + { + code: 'const foo = 0XaBcDeF', + output: 'const foo = 0xaBcDeF', + options: [{hexadecimalValue: 'ignore', radixIdentifier: 'lowercase'}], + }, + { + code: 'const foo = 0xaBcDeF', + output: 'const foo = 0XaBcDeF', + options: [{hexadecimalValue: 'ignore', radixIdentifier: 'uppercase'}], + }, + { + code: 'const foo = 0XaBcDeF', + output: 'const foo = 0XABCDEF', + options: [{hexadecimalValue: 'uppercase', radixIdentifier: 'ignore'}], + }, + { + code: 'const foo = 1.2E+3', + output: 'const foo = 1.2e+3', + options: [{hexadecimalValue: 'ignore', radixIdentifier: 'ignore'}], + }, + ].map(item => ({...item, errors: [error]})), }; test(tests); From 406bd83d98184b71768cd1890ea03e39512b4e30 Mon Sep 17 00:00:00 2001 From: Rantetsu Inori Date: Tue, 18 Feb 2025 02:52:06 +0800 Subject: [PATCH 03/14] Run lint --- rules/number-literal-case.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rules/number-literal-case.js b/rules/number-literal-case.js index be85c4ad87..2d41ef01a1 100644 --- a/rules/number-literal-case.js +++ b/rules/number-literal-case.js @@ -29,7 +29,7 @@ const convertCase = (raw, option) => { const fix = (raw, options) => { let fixed = raw; let isSpecialBase = false; // Indicates that the number is hexadecimal, octal, or binary. - fixed = fixed.replace(/^(0[xob])(.*)/i, (_, radix, value) => { + fixed = fixed.replace(/^(0[box])(.*)/i, (_, radix, value) => { isSpecialBase = true; radix = convertCase(radix, options.radixIdentifier); if (radix.toLowerCase() === '0x') { @@ -40,7 +40,7 @@ const fix = (raw, options) => { }); if (!isSpecialBase) { - fixed = fixed.replaceAll(/e/ig, expo => convertCase(expo, options.exponentialNotation)); + fixed = fixed.replaceAll(/e/gi, expo => convertCase(expo, options.exponentialNotation)); } return fixed; From 7f7a67022e6c07f82a88f9d08660dea9367943e7 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Tue, 18 Feb 2025 01:59:11 +0700 Subject: [PATCH 04/14] Update escape-case.md --- docs/rules/escape-case.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/rules/escape-case.md b/docs/rules/escape-case.md index 195a11d4a3..86d93e8b19 100644 --- a/docs/rules/escape-case.md +++ b/docs/rules/escape-case.md @@ -33,9 +33,9 @@ Type: `string`\ Default: `'uppercase'` - `'uppercase'` (default) - - Always use escape sequence values with uppercase characters. + - Always use escape sequence values with uppercase characters. - `'lowercase'` - - Always use escape sequence values with lowercase characters. + - Always use escape sequence values with lowercase characters. ```js // eslint unicorn/escape-case: ["error", "lowercase"] From 49853bff2f539ed5b4df9bc5106279994a2fe12d Mon Sep 17 00:00:00 2001 From: Rantetsu Inori Date: Tue, 18 Feb 2025 03:02:51 +0800 Subject: [PATCH 05/14] Update escape-case.md --- docs/rules/escape-case.md | 66 +++++++++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 13 deletions(-) diff --git a/docs/rules/escape-case.md b/docs/rules/escape-case.md index 86d93e8b19..cd115c3825 100644 --- a/docs/rules/escape-case.md +++ b/docs/rules/escape-case.md @@ -9,21 +9,37 @@ Enforces a consistent escaped value style by defining escape sequence values with uppercase or lowercase characters. The default style is uppercase, which promotes readability by making the escaped value more distinguishable from the identifier. -## Fail +## Examples ```js +// ❌ const foo = '\xa9'; -const foo = '\ud834'; -const foo = '\u{1d306}'; -const foo = '\ca'; -``` -## Pass +// ✅ +const foo = '\xA9'; +``` ```js -const foo = '\xA9'; +// ❌ +const foo = '\ud834'; + +// ✅ const foo = '\uD834'; +``` + +```js +// ❌ +const foo = '\u{1d306}'; + +// ✅ const foo = '\u{1D306}'; +``` + +```js +// ❌ +const foo = '\ca'; + +// ✅ const foo = '\cA'; ``` @@ -37,18 +53,42 @@ Default: `'uppercase'` - `'lowercase'` - Always use escape sequence values with lowercase characters. +Example: + ```js -// eslint unicorn/escape-case: ["error", "lowercase"] +{ + 'unicorn/escape-case': ['error', 'lowercase'] +} +``` -// Fail +```js +// ❌ const foo = '\xA9'; -const foo = '\uD834'; -const foo = '\u{1D306}'; -const foo = '\cA'; -// Pass +// ✅ const foo = '\xa9'; +``` + +```js +// ❌ +const foo = '\uD834'; + +// ✅ const foo = '\ud834'; +``` + +```js +// ❌ +const foo = '\u{1D306}'; + +// ✅ const foo = '\u{1d306}'; +``` + +```js +// ❌ +const foo = '\cA'; + +// ✅ const foo = '\ca'; ``` From 24daea89ee11f3a06fcf355593bcb14e8d29a109 Mon Sep 17 00:00:00 2001 From: Rantetsu Inori Date: Tue, 18 Feb 2025 03:11:41 +0800 Subject: [PATCH 06/14] Update number-literal-case.md --- docs/rules/number-literal-case.md | 55 ++++++++++++++++--------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/docs/rules/number-literal-case.md b/docs/rules/number-literal-case.md index d3284df31e..c72ea78916 100644 --- a/docs/rules/number-literal-case.md +++ b/docs/rules/number-literal-case.md @@ -17,52 +17,53 @@ Differentiating the casing of the identifier and value clearly separates them an [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; + +// ✅ +const foo = 0xFF; +const foo = 0xFFn; ``` [Binary](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Binary) ```js +// ❌ const foo = 0B10; const foo = 0B10n; + +// ✅ +const foo = 0b10; +const foo = 0b10n; ``` [Octal](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Octal) ```js +// ❌ const foo = 0O76; const foo = 0O76n; -``` - -Exponential notation - -```js -const foo = 2E-5; -``` - -## Pass - -```js -const foo = 0xFF; -``` - -```js -const foo = 0b10; -``` -```js +// ✅ const foo = 0o76; +const foo = 0o76n; ``` -```js -const foo = 0xFFn; -``` +[Exponential notation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Exponential) ```js +// ❌ +const foo = 2E-5; +const foo = 2E+5; +const foo = 2E5; + +// ✅ +const foo = 2e-5; const foo = 2e+5; +const foo = 2e5; ``` ## Options @@ -95,13 +96,13 @@ Example: ```js // eslint unicorn/number-literal-case: ["error", {"hexadecimalValue": "lowercase"}] -// Fail +// ❌ const foo = 0XFF; const foo = 0xFF; const foo = 0XFFn; const foo = 0xFFn; -// Pass +// ✅ const foo = 0Xff; const foo = 0xff; const foo = 0Xffn; @@ -119,7 +120,7 @@ Example: ```js // eslint unicorn/number-literal-case: ["error", {"radixIdentifier": "uppercase"}] -// Fail +// ❌ const foo = 0xFF; const foo = 0o76; const foo = 0b10; @@ -127,7 +128,7 @@ const foo = 0xFFn; const foo = 0o76n; const foo = 0b10n; -// Pass +// ✅ const foo = 0XFF; const foo = 0O76; const foo = 0B10; @@ -147,12 +148,12 @@ Example: ```js // eslint unicorn/number-literal-case: ["error", {"exponentialNotation": "uppercase"}] -// Fail +// ❌ const foo = 2e-5; const foo = 2e+5; const foo = 2e99; -// Pass +// ✅ const foo = 2E-5; const foo = 2E+5; const foo = 2E99; From 9de2fd5e4da118b61a0c7972b2749dc83c726477 Mon Sep 17 00:00:00 2001 From: Rantetsu Inori Date: Tue, 18 Feb 2025 03:15:17 +0800 Subject: [PATCH 07/14] Update number-literal-case.md Fix wrong option examples. --- docs/rules/number-literal-case.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/rules/number-literal-case.md b/docs/rules/number-literal-case.md index c72ea78916..73d1a9e9d4 100644 --- a/docs/rules/number-literal-case.md +++ b/docs/rules/number-literal-case.md @@ -94,7 +94,7 @@ Specify whether the hexadecimal number value (ABCDEF) should be in `uppercase`, Example: ```js -// eslint unicorn/number-literal-case: ["error", {"hexadecimalValue": "lowercase"}] +// eslint unicorn/number-literal-case: ["error", {"hexadecimalValue": "lowercase", "radixIdentifier": "ignore"}] // ❌ const foo = 0XFF; @@ -118,7 +118,7 @@ Specify whether the radix indentifer (`0x`, `0o`, `0b`) should be in `uppercase` Example: ```js -// eslint unicorn/number-literal-case: ["error", {"radixIdentifier": "uppercase"}] +// eslint unicorn/number-literal-case: ["error", {"radixIdentifier": "uppercase", "hexadecimalValue": "ignore"}] // ❌ const foo = 0xFF; From f0cf28d06a39491ba60328c5994700595606c0b1 Mon Sep 17 00:00:00 2001 From: Rantetsu Inori Date: Wed, 19 Feb 2025 16:57:27 +0800 Subject: [PATCH 08/14] Add disclaimer --- docs/rules/number-literal-case.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/rules/number-literal-case.md b/docs/rules/number-literal-case.md index 73d1a9e9d4..cf5c8aabe9 100644 --- a/docs/rules/number-literal-case.md +++ b/docs/rules/number-literal-case.md @@ -116,6 +116,8 @@ Default: `'lowercase'` Specify whether the radix indentifer (`0x`, `0o`, `0b`) should be in `uppercase`, `lowercase`, or `ignore` the check. Defaults to `'lowercase'`. +**Note: Adjusting this option to values other than `'lowercase'` may make your code unreadable, please use caution.** + Example: ```js // eslint unicorn/number-literal-case: ["error", {"radixIdentifier": "uppercase", "hexadecimalValue": "ignore"}] @@ -144,6 +146,8 @@ Default: `'lowercase'` Specify whether the exponential notation (`e`) should be in `uppercase`, `lowercase`, or `ignore` the check. Defaults to `'lowercase'`. +**Note: Adjusting this option to values other than `'lowercase'` may make your code unreadable, please use caution.** + Example: ```js // eslint unicorn/number-literal-case: ["error", {"exponentialNotation": "uppercase"}] From 2a9c91a19e043e3fa09c16405c021c58b441585a Mon Sep 17 00:00:00 2001 From: Rantetsu Inori Date: Mon, 3 Mar 2025 23:06:27 +0800 Subject: [PATCH 09/14] `number-literal-case`: drop radixIdentifier and exponentialNotation option --- docs/rules/number-literal-case.md | 84 +++-------------------- rules/number-literal-case.js | 47 ++----------- test/escape-case.js | 110 +++--------------------------- test/number-literal-case.js | 63 ++--------------- 4 files changed, 28 insertions(+), 276 deletions(-) diff --git a/docs/rules/number-literal-case.md b/docs/rules/number-literal-case.md index cf5c8aabe9..9530bb3efb 100644 --- a/docs/rules/number-literal-case.md +++ b/docs/rules/number-literal-case.md @@ -7,9 +7,10 @@ -Differentiating the casing of the identifier and value clearly separates them and makes your code more readable. The default style is: +Differentiating the casing of the identifier and value clearly separates them and makes your code more readable. -- 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 radix identifier `0x` `0o` `0b` 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). +- Uppercase or lowercase hexadecimal 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. ## Fail @@ -70,31 +71,18 @@ const foo = 2e5; Type: `object` -Default options: - -```js -{ - 'unicorn/number-literal-case': [ - 'error', - { - hexadecimalValue: 'uppercase', - radixIdentifier: 'lowercase', - exponentialNotation: 'lowercase' - } - ] -} -``` - ### hexadecimalValue -Type: `'uppercase' | 'lowercase' | 'ignore'`\ +Type: `'uppercase' | 'lowercase'`\ Default: `'uppercase'` -Specify whether the hexadecimal number value (ABCDEF) should be in `uppercase`, `lowercase`, or `ignore` the check. Defaults to `'uppercase'`. +Specify whether the hexadecimal number value (ABCDEF) should be in `uppercase` or `lowercase`. Defaults to `'uppercase'`. + +Note: `0x` is always lowercase and not controlled by this option to maintain readable code. Example: ```js -// eslint unicorn/number-literal-case: ["error", {"hexadecimalValue": "lowercase", "radixIdentifier": "ignore"}] +// eslint unicorn/number-literal-case: ["error", {"hexadecimalValue": "lowercase"}] // ❌ const foo = 0XFF; @@ -103,62 +91,6 @@ const foo = 0XFFn; const foo = 0xFFn; // ✅ -const foo = 0Xff; const foo = 0xff; -const foo = 0Xffn; const foo = 0xffn; ``` - -### radixIdentifier - -Type: `'uppercase' | 'lowercase' | 'ignore'`\ -Default: `'lowercase'` - -Specify whether the radix indentifer (`0x`, `0o`, `0b`) should be in `uppercase`, `lowercase`, or `ignore` the check. Defaults to `'lowercase'`. - -**Note: Adjusting this option to values other than `'lowercase'` may make your code unreadable, please use caution.** - -Example: -```js -// eslint unicorn/number-literal-case: ["error", {"radixIdentifier": "uppercase", "hexadecimalValue": "ignore"}] - -// ❌ -const foo = 0xFF; -const foo = 0o76; -const foo = 0b10; -const foo = 0xFFn; -const foo = 0o76n; -const foo = 0b10n; - -// ✅ -const foo = 0XFF; -const foo = 0O76; -const foo = 0B10; -const foo = 0XFFn; -const foo = 0O76n; -const foo = 0B10n; -``` - -### exponentialNotation - -Type: `'uppercase' | 'lowercase' | 'ignore'`\ -Default: `'lowercase'` - -Specify whether the exponential notation (`e`) should be in `uppercase`, `lowercase`, or `ignore` the check. Defaults to `'lowercase'`. - -**Note: Adjusting this option to values other than `'lowercase'` may make your code unreadable, please use caution.** - -Example: -```js -// eslint unicorn/number-literal-case: ["error", {"exponentialNotation": "uppercase"}] - -// ❌ -const foo = 2e-5; -const foo = 2e+5; -const foo = 2e99; - -// ✅ -const foo = 2E-5; -const foo = 2E+5; -const foo = 2E99; -``` diff --git a/rules/number-literal-case.js b/rules/number-literal-case.js index 2d41ef01a1..8970ef4793 100644 --- a/rules/number-literal-case.js +++ b/rules/number-literal-case.js @@ -6,41 +6,14 @@ const messages = { [MESSAGE_ID]: 'Invalid number literal casing.', }; -/** - @param {string} raw - @param {Options[keyof Options]} option - */ -const convertCase = (raw, option) => { - if (option === 'uppercase') { - return raw.toUpperCase(); - } - - if (option === 'lowercase') { - return raw.toLowerCase(); - } - - return raw; -}; - /** @param {string} raw @param {Options} options */ -const fix = (raw, options) => { - let fixed = raw; - let isSpecialBase = false; // Indicates that the number is hexadecimal, octal, or binary. - fixed = fixed.replace(/^(0[box])(.*)/i, (_, radix, value) => { - isSpecialBase = true; - radix = convertCase(radix, options.radixIdentifier); - if (radix.toLowerCase() === '0x') { - value = convertCase(value, options.hexadecimalValue); - } - - return radix + value; - }); - - if (!isSpecialBase) { - fixed = fixed.replaceAll(/e/gi, expo => convertCase(expo, options.exponentialNotation)); +const fix = (raw, {hexadecimalValue}) => { + let fixed = raw.toLowerCase(); + if (fixed.startsWith('0x')) { + fixed = '0x' + fixed.slice(2)[hexadecimalValue === 'lowercase' ? 'toLowerCase' : 'toUpperCase'](); } return fixed; @@ -54,8 +27,6 @@ const create = context => ({ /** @type {Options} */ const options = context.options[0] ?? {}; options.hexadecimalValue ??= 'uppercase'; - options.radixIdentifier ??= 'lowercase'; - options.exponentialNotation ??= 'lowercase'; let fixed = raw; if (isNumberLiteral(node)) { @@ -77,11 +48,7 @@ const create = context => ({ /** @typedef {Record} Options */ const caseEnum = /** @type {const} */ ({ - enum: [ - 'uppercase', - 'lowercase', - 'ignore', - ], + enum: ['uppercase', 'lowercase'], }); const schema = [ @@ -90,8 +57,6 @@ const schema = [ additionalProperties: false, properties: { hexadecimalValue: caseEnum, - radixIdentifier: caseEnum, - exponentialNotation: caseEnum, }, }, ]; @@ -109,8 +74,6 @@ const config = { schema, defaultOptions: [{ hexadecimalValue: 'uppercase', - radixIdentifier: 'lowercase', - exponentialNotation: 'lowercase', }], messages, }, diff --git a/test/escape-case.js b/test/escape-case.js index 2d205c018f..b8b5aaac70 100644 --- a/test/escape-case.js +++ b/test/escape-case.js @@ -3,14 +3,8 @@ import {getTester} from './utils/test.js'; const {test} = getTester(import.meta); -const uppercaseTest = { - errors: [{messageId: 'escape-uppercase'}], -}; - -const lowercaseTest = { - options: ['lowercase'], - errors: [{messageId: 'escape-lowercase'}], -}; +const MESSAGE_ID_UPPERCASE = 'escape-uppercase'; +const MESSAGE_ID_LOWERCASE = 'escape-lowercase'; // 'uppercase' test({ @@ -71,242 +65,199 @@ test({ { code: String.raw`const foo = "\xa9";`, output: String.raw`const foo = "\xA9";`, - ...uppercaseTest, }, // Mixed cases { code: String.raw`const foo = "\xAa";`, output: String.raw`const foo = "\xAA";`, - ...uppercaseTest, }, { code: String.raw`const foo = "\uAaAa";`, output: String.raw`const foo = "\uAAAA";`, - ...uppercaseTest, }, { code: String.raw`const foo = "\u{AaAa}";`, output: String.raw`const foo = "\u{AAAA}";`, - ...uppercaseTest, }, // Many { code: String.raw`const foo = "\xAab\xaab\xAAb\uAaAab\uaaaab\uAAAAb\u{AaAa}b\u{aaaa}b\u{AAAA}";`, output: String.raw`const foo = "\xAAb\xAAb\xAAb\uAAAAb\uAAAAb\uAAAAb\u{AAAA}b\u{AAAA}b\u{AAAA}";`, - ...uppercaseTest, }, { code: String.raw`const foo = "\ud834";`, output: String.raw`const foo = "\uD834";`, - ...uppercaseTest, }, { code: String.raw`const foo = "\u{1d306}";`, output: String.raw`const foo = "\u{1D306}";`, - ...uppercaseTest, }, { code: String.raw`const foo = "\ud834foo";`, output: String.raw`const foo = "\uD834foo";`, - ...uppercaseTest, }, { code: String.raw`const foo = "foo\ud834";`, output: String.raw`const foo = "foo\uD834";`, - ...uppercaseTest, }, { code: String.raw`const foo = "foo \ud834";`, output: String.raw`const foo = "foo \uD834";`, - ...uppercaseTest, }, { code: String.raw`const foo = "\\\ud834foo";`, output: String.raw`const foo = "\\\uD834foo";`, - ...uppercaseTest, }, { code: String.raw`const foo = "foo\\\ud834";`, output: String.raw`const foo = "foo\\\uD834";`, - ...uppercaseTest, }, { code: String.raw`const foo = "foo \\\ud834";`, output: String.raw`const foo = "foo \\\uD834";`, - ...uppercaseTest, }, // TemplateLiteral { code: 'const foo = `\\xa9`;', output: 'const foo = `\\xA9`;', - ...uppercaseTest, }, { code: 'const foo = `\\ud834`;', output: 'const foo = `\\uD834`;', - ...uppercaseTest, }, { code: 'const foo = `\\u{1d306}`;', output: 'const foo = `\\u{1D306}`;', - ...uppercaseTest, }, { code: 'const foo = `\\ud834foo`;', output: 'const foo = `\\uD834foo`;', - ...uppercaseTest, }, { code: 'const foo = `foo\\ud834`;', output: 'const foo = `foo\\uD834`;', - ...uppercaseTest, }, { code: 'const foo = `foo \\ud834`;', output: 'const foo = `foo \\uD834`;', - ...uppercaseTest, }, { code: 'const foo = `${"\ud834 foo"} \\ud834`;', output: 'const foo = `${"\uD834 foo"} \\uD834`;', - ...uppercaseTest, }, { code: 'const foo = `\\ud834${foo}\\ud834${foo}\\ud834`;', output: 'const foo = `\\uD834${foo}\\uD834${foo}\\uD834`;', - ...uppercaseTest, - errors: Array.from({length: 3}, () => uppercaseTest.errors[0]), + errors: Array.from({length: 3}, () => ({messageId: MESSAGE_ID_UPPERCASE})), }, { code: 'const foo = `\\\\\\ud834foo`;', output: 'const foo = `\\\\\\uD834foo`;', - ...uppercaseTest, }, { code: 'const foo = `foo\\\\\\ud834`;', output: 'const foo = `foo\\\\\\uD834`;', - ...uppercaseTest, }, { code: 'const foo = `foo \\\\\\ud834`;', output: 'const foo = `foo \\\\\\uD834`;', - ...uppercaseTest, }, // TODO: This is not safe, it will be broken if `tagged` uses `arguments[0].raw` // #2341 { code: 'const foo = tagged`\\uAaAa`;', output: 'const foo = tagged`\\uAAAA`;', - ...uppercaseTest, }, { code: 'const foo = `\\uAaAa```;', output: 'const foo = `\\uAAAA```;', - ...uppercaseTest, }, // Mixed cases { code: 'const foo = `\\xAa`;', output: 'const foo = `\\xAA`;', - ...uppercaseTest, }, { code: 'const foo = `\\uAaAa`;', output: 'const foo = `\\uAAAA`;', - ...uppercaseTest, }, { code: 'const foo = `\\u{AaAa}`;', output: 'const foo = `\\u{AAAA}`;', - ...uppercaseTest, }, // Many { code: 'const foo = `\\xAab\\xaab\\xAA${foo}\\uAaAab\\uaaaab\\uAAAAb\\u{AaAa}${foo}\\u{aaaa}b\\u{AAAA}`;', output: 'const foo = `\\xAAb\\xAAb\\xAA${foo}\\uAAAAb\\uAAAAb\\uAAAAb\\u{AAAA}${foo}\\u{AAAA}b\\u{AAAA}`;', - ...uppercaseTest, - errors: Array.from({length: 3}, () => uppercaseTest.errors[0]), + errors: Array.from({length: 3}, () => ({messageId: MESSAGE_ID_UPPERCASE})), }, // Literal regex { code: String.raw`const foo = /\xa9/;`, output: String.raw`const foo = /\xA9/;`, - ...uppercaseTest, }, { code: String.raw`const foo = /\ud834/`, output: String.raw`const foo = /\uD834/`, - ...uppercaseTest, }, { code: String.raw`const foo = /\u{1d306}/u`, output: String.raw`const foo = /\u{1D306}/u`, - ...uppercaseTest, }, { code: String.raw`const foo = /\ca/`, output: String.raw`const foo = /\cA/`, - ...uppercaseTest, }, { code: String.raw`const foo = /foo\\\xa9/;`, output: String.raw`const foo = /foo\\\xA9/;`, - ...uppercaseTest, }, { code: String.raw`const foo = /foo\\\\\xa9/;`, output: String.raw`const foo = /foo\\\\\xA9/;`, - ...uppercaseTest, }, // Mixed cases { code: String.raw`const foo = /\xAa/;`, output: String.raw`const foo = /\xAA/;`, - ...uppercaseTest, }, { code: String.raw`const foo = /\uAaAa/;`, output: String.raw`const foo = /\uAAAA/;`, - ...uppercaseTest, }, { code: String.raw`const foo = /\u{AaAa}/;`, output: String.raw`const foo = /\u{AAAA}/;`, - ...uppercaseTest, }, // Many { code: String.raw`const foo = /\xAab\xaab\xAAb\uAaAab\uaaaab\uAAAAb\u{AaAa}b\u{aaaa}b\u{AAAA}b\ca/;`, output: String.raw`const foo = /\xAAb\xAAb\xAAb\uAAAAb\uAAAAb\uAAAAb\u{AAAA}b\u{AAAA}b\u{AAAA}b\cA/;`, - ...uppercaseTest, }, // RegExp { code: String.raw`const foo = new RegExp("/\xa9")`, output: String.raw`const foo = new RegExp("/\xA9")`, - ...uppercaseTest, }, { code: String.raw`const foo = new RegExp("/\ud834/")`, output: String.raw`const foo = new RegExp("/\uD834/")`, - ...uppercaseTest, }, { code: String.raw`const foo = new RegExp("/\u{1d306}/", "u")`, output: String.raw`const foo = new RegExp("/\u{1D306}/", "u")`, - ...uppercaseTest, }, - ], + ].map(item => ({errors: [{messageId: MESSAGE_ID_UPPERCASE}], ...item})), }); // 'lowercase' @@ -358,246 +309,203 @@ test({ String.raw`const foo = new RegExp("/\u{1d306}/", "u")`, String.raw`const foo = new RegExp("/\ca/")`, String.raw`const foo = new RegExp("/\cA/")`, - ].map(code => ({code, options: lowercaseTest.options})), + ].map(code => ({code, options: ['lowercase']})), invalid: [ // Literal string { code: String.raw`const foo = "\xA9";`, output: String.raw`const foo = "\xa9";`, - ...lowercaseTest, }, // Mixed cases { code: String.raw`const foo = "\xaA";`, output: String.raw`const foo = "\xaa";`, - ...lowercaseTest, }, { code: String.raw`const foo = "\uaAaA";`, output: String.raw`const foo = "\uaaaa";`, - ...lowercaseTest, }, { code: String.raw`const foo = "\u{aAaA}";`, output: String.raw`const foo = "\u{aaaa}";`, - ...lowercaseTest, }, // Many { code: String.raw`const foo = "\xAab\xaAb\xaab\xAAb\uAaAab\uaaaab\uAAAAb\u{AaAa}b\u{aaaa}b\u{AAAA}";`, output: String.raw`const foo = "\xaab\xaab\xaab\xaab\uaaaab\uaaaab\uaaaab\u{aaaa}b\u{aaaa}b\u{aaaa}";`, - ...lowercaseTest, }, { code: String.raw`const foo = "\uD834";`, output: String.raw`const foo = "\ud834";`, - ...lowercaseTest, }, { code: String.raw`const foo = "\u{1D306}";`, output: String.raw`const foo = "\u{1d306}";`, - ...lowercaseTest, }, { code: String.raw`const foo = "\uD834FOO";`, output: String.raw`const foo = "\ud834FOO";`, - ...lowercaseTest, }, { code: String.raw`const foo = "FOO\uD834";`, output: String.raw`const foo = "FOO\ud834";`, - ...lowercaseTest, }, { code: String.raw`const foo = "FOO \uD834";`, output: String.raw`const foo = "FOO \ud834";`, - ...lowercaseTest, }, { code: String.raw`const foo = "\\\uD834FOO";`, output: String.raw`const foo = "\\\ud834FOO";`, - ...lowercaseTest, }, { code: String.raw`const foo = "FOO\\\uD834";`, output: String.raw`const foo = "FOO\\\ud834";`, - ...lowercaseTest, }, { code: String.raw`const foo = "FOO \\\uD834";`, output: String.raw`const foo = "FOO \\\ud834";`, - ...lowercaseTest, }, // TemplateLiteral { code: 'const foo = `\\xA9`;', output: 'const foo = `\\xa9`;', - ...lowercaseTest, }, { code: 'const foo = `\\uD834`;', output: 'const foo = `\\ud834`;', - ...lowercaseTest, }, { code: 'const foo = `\\u{1D306}`;', output: 'const foo = `\\u{1d306}`;', - ...lowercaseTest, }, { code: 'const foo = `\\uD834FOO`;', output: 'const foo = `\\ud834FOO`;', - ...lowercaseTest, }, { code: 'const foo = `FOO\\uD834`;', output: 'const foo = `FOO\\ud834`;', - ...lowercaseTest, }, { code: 'const foo = `FOO \\uD834`;', output: 'const foo = `FOO \\ud834`;', - ...lowercaseTest, }, { code: 'const foo = `${"\uD834 FOO"} \\uD834`;', output: 'const foo = `${"\ud834 FOO"} \\ud834`;', - ...lowercaseTest, }, { code: 'const foo = `\\uD834${FOO}\\uD834${FOO}\\uD834`;', output: 'const foo = `\\ud834${FOO}\\ud834${FOO}\\ud834`;', - ...lowercaseTest, - errors: Array.from({length: 3}, () => lowercaseTest.errors[0]), + errors: Array.from({length: 3}, () => ({messageId: MESSAGE_ID_LOWERCASE})), }, { code: 'const foo = `\\\\\\uD834FOO`;', output: 'const foo = `\\\\\\ud834FOO`;', - ...lowercaseTest, }, { code: 'const foo = `FOO\\\\\\uD834`;', output: 'const foo = `FOO\\\\\\ud834`;', - ...lowercaseTest, }, { code: 'const foo = `FOO \\\\\\uD834`;', output: 'const foo = `FOO \\\\\\ud834`;', - ...lowercaseTest, }, // TODO: This is not safe, it will be broken if `tagged` uses `arguments[0].raw` // #2341 { code: 'const foo = tagged`\\uaAaA`;', output: 'const foo = tagged`\\uaaaa`;', - ...lowercaseTest, }, { code: 'const foo = `\\uaAaA```;', output: 'const foo = `\\uaaaa```;', - ...lowercaseTest, }, // Mixed cases { code: 'const foo = `\\xaA`;', output: 'const foo = `\\xaa`;', - ...lowercaseTest, }, { code: 'const foo = `\\uaAaA`;', output: 'const foo = `\\uaaaa`;', - ...lowercaseTest, }, { code: 'const foo = `\\u{aAaA}`;', output: 'const foo = `\\u{aaaa}`;', - ...lowercaseTest, }, // Many { code: 'const foo = `\\xAab\\xaab\\xaAb\\xAA${foo}\\uAaAab\\uaaaab\\uAAAAb\\u{AaAa}${foo}\\u{aaaa}b\\u{AAAA}`;', output: 'const foo = `\\xaab\\xaab\\xaab\\xaa${foo}\\uaaaab\\uaaaab\\uaaaab\\u{aaaa}${foo}\\u{aaaa}b\\u{aaaa}`;', - ...lowercaseTest, - errors: Array.from({length: 3}, () => lowercaseTest.errors[0]), + errors: Array.from({length: 3}, () => ({messageId: MESSAGE_ID_LOWERCASE})), }, // Literal regex { code: String.raw`const foo = /\xA9/;`, output: String.raw`const foo = /\xa9/;`, - ...lowercaseTest, }, { code: String.raw`const foo = /\uD834/`, output: String.raw`const foo = /\ud834/`, - ...lowercaseTest, }, { code: String.raw`const foo = /\u{1D306}/u`, output: String.raw`const foo = /\u{1d306}/u`, - ...lowercaseTest, }, { code: String.raw`const foo = /\cA/`, output: String.raw`const foo = /\ca/`, - ...lowercaseTest, }, { code: String.raw`const foo = /FOO\\\xA9/;`, output: String.raw`const foo = /FOO\\\xa9/;`, - ...lowercaseTest, }, { code: String.raw`const foo = /FOO\\\\\xA9/;`, output: String.raw`const foo = /FOO\\\\\xa9/;`, - ...lowercaseTest, }, // Mixed cases { code: String.raw`const foo = /\xaA/;`, output: String.raw`const foo = /\xaa/;`, - ...lowercaseTest, }, { code: String.raw`const foo = /\uaAaA/;`, output: String.raw`const foo = /\uaaaa/;`, - ...lowercaseTest, }, { code: String.raw`const foo = /\u{aAaA}/;`, output: String.raw`const foo = /\u{aaaa}/;`, - ...lowercaseTest, }, // Many { code: String.raw`const foo = /\xAab\xaAb\xaab\xAAb\uAaAab\uaaaab\uAAAAb\u{AaAa}b\u{aaaa}b\u{AAAA}b\cA/;`, output: String.raw`const foo = /\xaab\xaab\xaab\xaab\uaaaab\uaaaab\uaaaab\u{aaaa}b\u{aaaa}b\u{aaaa}b\ca/;`, - ...lowercaseTest, }, // RegExp { code: String.raw`const foo = new RegExp("/\xA9")`, output: String.raw`const foo = new RegExp("/\xa9")`, - ...lowercaseTest, }, { code: String.raw`const foo = new RegExp("/\uD834/")`, output: String.raw`const foo = new RegExp("/\ud834/")`, - ...lowercaseTest, }, { code: String.raw`const foo = new RegExp("/\u{1D306}/", "u")`, output: String.raw`const foo = new RegExp("/\u{1d306}/", "u")`, - ...lowercaseTest, }, - ], + ].map(item => ({errors: [{messageId: MESSAGE_ID_LOWERCASE}], ...item, options: ['lowercase']})), }); diff --git a/test/number-literal-case.js b/test/number-literal-case.js index 9008251e21..e3f5ce51f8 100644 --- a/test/number-literal-case.js +++ b/test/number-literal-case.js @@ -168,70 +168,19 @@ const tests = { code: 'const foo = 0XaBcDeF', output: 'const foo = 0xabcdef', }, - { - code: 'const foo = 0XaBcDeFn', - output: 'const foo = 0xabcdefn', - }, - ].map(item => ({...item, options: [{hexadecimalValue: 'lowercase'}]})), - - // Uppercase radix indentifer - ...[ - { - code: 'const foo = 0b10', - output: 'const foo = 0B10', - }, - { - code: 'const foo = 0o1234567', - output: 'const foo = 0O1234567', - }, { code: 'const foo = 0xaBcDeF', - output: 'const foo = 0XABCDEF', - }, - ].map(item => ({...item, options: [{radixIdentifier: 'uppercase'}]})), - - // Uppercase exponential notation - ...[ - { - code: 'const foo = 1.2e3', - output: 'const foo = 1.2E3', + output: 'const foo = 0xabcdef', }, { - code: 'const foo = 1.2e-3', - output: 'const foo = 1.2E-3', + code: 'const foo = 0XaBcDeFn', + output: 'const foo = 0xabcdefn', }, { - code: 'const foo = 1.2e+3', - output: 'const foo = 1.2E+3', + code: 'const foo = 0XdeEd_Beefn', + output: 'const foo = 0xdeed_beefn', }, - ].map(item => ({...item, options: [{exponentialNotation: 'uppercase'}]})), - - // Mixed options - { - code: 'const foo = 0xaBcDeF', - output: 'const foo = 0Xabcdef', - options: [{hexadecimalValue: 'lowercase', radixIdentifier: 'uppercase'}], - }, - { - code: 'const foo = 0XaBcDeF', - output: 'const foo = 0xaBcDeF', - options: [{hexadecimalValue: 'ignore', radixIdentifier: 'lowercase'}], - }, - { - code: 'const foo = 0xaBcDeF', - output: 'const foo = 0XaBcDeF', - options: [{hexadecimalValue: 'ignore', radixIdentifier: 'uppercase'}], - }, - { - code: 'const foo = 0XaBcDeF', - output: 'const foo = 0XABCDEF', - options: [{hexadecimalValue: 'uppercase', radixIdentifier: 'ignore'}], - }, - { - code: 'const foo = 1.2E+3', - output: 'const foo = 1.2e+3', - options: [{hexadecimalValue: 'ignore', radixIdentifier: 'ignore'}], - }, + ].map(item => ({...item, options: [{hexadecimalValue: 'lowercase'}]})), ].map(item => ({...item, errors: [error]})), }; From 8f82ca474d02f2297e6fda040edb911a7ba590e1 Mon Sep 17 00:00:00 2001 From: Rantetsu Inori Date: Mon, 3 Mar 2025 23:08:01 +0800 Subject: [PATCH 10/14] `number-literal-case`: Adjust the example to make it clearer --- docs/rules/number-literal-case.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/rules/number-literal-case.md b/docs/rules/number-literal-case.md index 9530bb3efb..1bb007cff7 100644 --- a/docs/rules/number-literal-case.md +++ b/docs/rules/number-literal-case.md @@ -81,9 +81,19 @@ Specify whether the hexadecimal number value (ABCDEF) should be in `uppercase` o Note: `0x` is always lowercase and not controlled by this option to maintain readable code. Example: + ```js -// eslint unicorn/number-literal-case: ["error", {"hexadecimalValue": "lowercase"}] +{ + 'unicorn/number-literal-case': [ + 'error', + { + hexadecimalValue: 'lowercase', + } + ] +} +``` +```js // ❌ const foo = 0XFF; const foo = 0xFF; From ceb56af6bf00db7442676e3e1573da0b7abb18b5 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Tue, 4 Mar 2025 15:25:06 +0700 Subject: [PATCH 11/14] Update number-literal-case.md --- docs/rules/number-literal-case.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rules/number-literal-case.md b/docs/rules/number-literal-case.md index 1bb007cff7..e08ab3d12c 100644 --- a/docs/rules/number-literal-case.md +++ b/docs/rules/number-literal-case.md @@ -76,7 +76,7 @@ Type: `object` Type: `'uppercase' | 'lowercase'`\ Default: `'uppercase'` -Specify whether the hexadecimal number value (ABCDEF) should be in `uppercase` or `lowercase`. Defaults to `'uppercase'`. +Specify whether the hexadecimal number value (ABCDEF) should be in `uppercase` or `lowercase`. Note: `0x` is always lowercase and not controlled by this option to maintain readable code. From 571c99da5663c936c71f962c9ef4f4538ed0a85a Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Tue, 4 Mar 2025 15:25:24 +0700 Subject: [PATCH 12/14] Update number-literal-case.js --- rules/number-literal-case.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rules/number-literal-case.js b/rules/number-literal-case.js index 8970ef4793..44175ef315 100644 --- a/rules/number-literal-case.js +++ b/rules/number-literal-case.js @@ -7,9 +7,9 @@ const messages = { }; /** - @param {string} raw - @param {Options} options - */ +@param {string} raw +@param {Options} options +*/ const fix = (raw, {hexadecimalValue}) => { let fixed = raw.toLowerCase(); if (fixed.startsWith('0x')) { From 4cdbe5aa337e77e02520a8abbddb49a5f55f65ac Mon Sep 17 00:00:00 2001 From: Rantetsu Inori Date: Tue, 4 Mar 2025 17:40:50 +0800 Subject: [PATCH 13/14] Fix a markdown lint issue --- docs/rules/escape-case.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/rules/escape-case.md b/docs/rules/escape-case.md index cd115c3825..4111bd1a82 100644 --- a/docs/rules/escape-case.md +++ b/docs/rules/escape-case.md @@ -49,9 +49,9 @@ Type: `string`\ Default: `'uppercase'` - `'uppercase'` (default) - - Always use escape sequence values with uppercase characters. + - Always use escape sequence values with uppercase characters. - `'lowercase'` - - Always use escape sequence values with lowercase characters. + - Always use escape sequence values with lowercase characters. Example: From 9e062f81005a02f71dcd447c91e729ab434d23d4 Mon Sep 17 00:00:00 2001 From: Rantetsu Inori Date: Tue, 4 Mar 2025 17:44:56 +0800 Subject: [PATCH 14/14] Fix eslint docs --- docs/rules/escape-case.md | 2 +- readme.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/rules/escape-case.md b/docs/rules/escape-case.md index 4111bd1a82..1a69f72f5e 100644 --- a/docs/rules/escape-case.md +++ b/docs/rules/escape-case.md @@ -1,4 +1,4 @@ -# Require escape sequences to use uppercase values +# Require escape sequences to use uppercase or lowercase values 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs-eslintconfigjs). diff --git a/readme.md b/readme.md index 85b8805d5d..bf82401e35 100644 --- a/readme.md +++ b/readme.md @@ -67,7 +67,7 @@ export default [ | [custom-error-definition](docs/rules/custom-error-definition.md) | Enforce correct `Error` subclassing. | | 🔧 | | | [empty-brace-spaces](docs/rules/empty-brace-spaces.md) | Enforce no spaces between braces. | ✅ | 🔧 | | | [error-message](docs/rules/error-message.md) | Enforce passing a `message` value when creating a built-in error. | ✅ | | | -| [escape-case](docs/rules/escape-case.md) | Require escape sequences to use uppercase values. | ✅ | 🔧 | | +| [escape-case](docs/rules/escape-case.md) | Require escape sequences to use uppercase or lowercase values. | ✅ | 🔧 | | | [expiring-todo-comments](docs/rules/expiring-todo-comments.md) | Add expiration conditions to TODO comments. | ✅ | | | | [explicit-length-check](docs/rules/explicit-length-check.md) | Enforce explicitly comparing the `length` or `size` property of a value. | ✅ | 🔧 | 💡 | | [filename-case](docs/rules/filename-case.md) | Enforce a case style for filenames. | ✅ | | |