diff --git a/docs/rules/text-encoding-identifier-case.md b/docs/rules/text-encoding-identifier-case.md index 7492995349..ba5f7245a3 100644 --- a/docs/rules/text-encoding-identifier-case.md +++ b/docs/rules/text-encoding-identifier-case.md @@ -37,3 +37,26 @@ const string = buffer.toString('utf-8'); // ✅ const string = buffer.toString('utf8'); ``` + +## Options + +Type: `object` + +### withDash + +Type: `boolean`\ +Default: `false` + +Use WHATWG standard encoding notation with dashes (e.g., ⁠`utf-8` instead of `⁠utf8`). + +```js +// ❌ +/* eslint unicorn/text-encoding-identifier-case: ["error", {"withDash": true}] */ +await fs.readFile(file, 'utf8'); +``` + +```js +// ✅ +/* eslint unicorn/text-encoding-identifier-case: ["error", {"withDash": true}] */ +await fs.readFile(file, 'utf-8'); +``` diff --git a/rules/text-encoding-identifier-case.js b/rules/text-encoding-identifier-case.js index 5f2b7818fd..037a9d880b 100644 --- a/rules/text-encoding-identifier-case.js +++ b/rules/text-encoding-identifier-case.js @@ -8,12 +8,13 @@ const messages = { [MESSAGE_ID_SUGGESTION]: 'Replace `{{value}}` with `{{replacement}}`.', }; -const getReplacement = encoding => { +const getReplacement = (encoding, withDash) => { switch (encoding.toLowerCase()) { // eslint-disable-next-line unicorn/text-encoding-identifier-case case 'utf-8': case 'utf8': { - return 'utf8'; + // eslint-disable-next-line unicorn/text-encoding-identifier-case + return withDash ? 'utf-8' : 'utf8'; } case 'ascii': { @@ -34,8 +35,12 @@ const isFsReadFileEncoding = node => && node.parent.arguments[0].type !== 'SpreadElement'; /** @param {import('eslint').Rule.RuleContext} context */ -const create = () => ({ - Literal(node) { +const create = context => { + const { + withDash, + } = context.options[0]; + + context.on('Literal', node => { if (typeof node.value !== 'string') { return; } @@ -58,7 +63,7 @@ const create = () => ({ const {raw} = node; const value = raw.slice(1, -1); - const replacement = getReplacement(value); + const replacement = getReplacement(value, withDash); if (!replacement || replacement === value) { return; } @@ -88,8 +93,20 @@ const create = () => ({ ]; return problem; + }); +}; + +const schema = [ + { + type: 'object', + additionalProperties: false, + properties: { + withDash: { + type: 'boolean', + }, + }, }, -}); +]; /** @type {import('eslint').Rule.RuleModule} */ const config = { @@ -102,6 +119,10 @@ const config = { }, fixable: 'code', hasSuggestions: true, + schema, + defaultOptions: [{ + withDash: false, + }], messages, }, }; diff --git a/test/snapshots/text-encoding-identifier-case.js.md b/test/snapshots/text-encoding-identifier-case.js.md index 4a7984e3fd..00c8858638 100644 --- a/test/snapshots/text-encoding-identifier-case.js.md +++ b/test/snapshots/text-encoding-identifier-case.js.md @@ -375,7 +375,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Prefer \`utf8\` over \`UTF-8\`.␊ ` -## invalid(1): +## invalid(20): > Input @@ -394,7 +394,7 @@ Generated by [AVA](https://avajs.dev). 1 | ␊ ` -## invalid(2): +## invalid(21): > Input @@ -413,7 +413,7 @@ Generated by [AVA](https://avajs.dev). 1 | ␊ ` -## invalid(3): +## invalid(22): > Input @@ -432,7 +432,7 @@ Generated by [AVA](https://avajs.dev). 1 | ␊ ` -## invalid(4): +## invalid(23): > Input @@ -450,3 +450,355 @@ Generated by [AVA](https://avajs.dev). Suggestion 1/1: Replace \`ASCII\` with \`ascii\`.␊ 1 | ␊ ` + +## invalid(1): "UTF-8"; + +> Input + + `␊ + 1 | "UTF-8";␊ + ` + +> Options + + `␊ + [␊ + {␊ + "withDash": true␊ + }␊ + ]␊ + ` + +> Error 1/1 + + `␊ + > 1 | "UTF-8";␊ + | ^^^^^^^ Prefer \`utf-8\` over \`UTF-8\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Replace \`UTF-8\` with \`utf-8\`.␊ + 1 | "utf-8";␊ + ` + +## invalid(2): "UTF8"; + +> Input + + `␊ + 1 | "UTF8";␊ + ` + +> Options + + `␊ + [␊ + {␊ + "withDash": true␊ + }␊ + ]␊ + ` + +> Error 1/1 + + `␊ + > 1 | "UTF8";␊ + | ^^^^^^ Prefer \`utf-8\` over \`UTF8\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Replace \`UTF8\` with \`utf-8\`.␊ + 1 | "utf-8";␊ + ` + +## invalid(3): "utf8"; + +> Input + + `␊ + 1 | "utf8";␊ + ` + +> Options + + `␊ + [␊ + {␊ + "withDash": true␊ + }␊ + ]␊ + ` + +> Error 1/1 + + `␊ + > 1 | "utf8";␊ + | ^^^^^^ Prefer \`utf-8\` over \`utf8\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Replace \`utf8\` with \`utf-8\`.␊ + 1 | "utf-8";␊ + ` + +## invalid(4): 'utf8'; + +> Input + + `␊ + 1 | 'utf8';␊ + ` + +> Options + + `␊ + [␊ + {␊ + "withDash": true␊ + }␊ + ]␊ + ` + +> Error 1/1 + + `␊ + > 1 | 'utf8';␊ + | ^^^^^^ Prefer \`utf-8\` over \`utf8\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Replace \`utf8\` with \`utf-8\`.␊ + 1 | 'utf-8';␊ + ` + +## invalid(5): "Utf8"; + +> Input + + `␊ + 1 | "Utf8";␊ + ` + +> Options + + `␊ + [␊ + {␊ + "withDash": true␊ + }␊ + ]␊ + ` + +> Error 1/1 + + `␊ + > 1 | "Utf8";␊ + | ^^^^^^ Prefer \`utf-8\` over \`Utf8\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Replace \`Utf8\` with \`utf-8\`.␊ + 1 | "utf-8";␊ + ` + +## invalid(6): "ASCII"; + +> Input + + `␊ + 1 | "ASCII";␊ + ` + +> Options + + `␊ + [␊ + {␊ + "withDash": true␊ + }␊ + ]␊ + ` + +> Error 1/1 + + `␊ + > 1 | "ASCII";␊ + | ^^^^^^^ Prefer \`ascii\` over \`ASCII\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Replace \`ASCII\` with \`ascii\`.␊ + 1 | "ascii";␊ + ` + +## invalid(7): fs.readFile(file, "utf8",); + +> Input + + `␊ + 1 | fs.readFile(file, "utf8",);␊ + ` + +> Options + + `␊ + [␊ + {␊ + "withDash": true␊ + }␊ + ]␊ + ` + +> Output + + `␊ + 1 | fs.readFile(file, "utf-8",);␊ + ` + +> Error 1/1 + + `␊ + > 1 | fs.readFile(file, "utf8",);␊ + | ^^^^^^ Prefer \`utf-8\` over \`utf8\`.␊ + ` + +## invalid(8): whatever.readFile(file, "UTF8",) + +> Input + + `␊ + 1 | whatever.readFile(file, "UTF8",)␊ + ` + +> Options + + `␊ + [␊ + {␊ + "withDash": true␊ + }␊ + ]␊ + ` + +> Output + + `␊ + 1 | whatever.readFile(file, "utf-8",)␊ + ` + +> Error 1/1 + + `␊ + > 1 | whatever.readFile(file, "UTF8",)␊ + | ^^^^^^ Prefer \`utf-8\` over \`UTF8\`.␊ + ` + +## invalid(9): + +> Input + + `␊ + 1 | ␊ + ` + +> Options + + `␊ + [␊ + {␊ + "withDash": true␊ + }␊ + ]␊ + ` + +> Error 1/1 + + `␊ + > 1 | ␊ + | ^^^^^^ Prefer \`utf-8\` over \`utf8\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Replace \`utf8\` with \`utf-8\`.␊ + 1 | ␊ + ` + +## invalid(10): + +> Input + + `␊ + 1 | ␊ + ` + +> Options + + `␊ + [␊ + {␊ + "withDash": true␊ + }␊ + ]␊ + ` + +> Error 1/1 + + `␊ + > 1 | ␊ + | ^^^^^^ Prefer \`utf-8\` over \`utf8\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Replace \`utf8\` with \`utf-8\`.␊ + 1 | ␊ + ` + +## invalid(11): + +> Input + + `␊ + 1 | ␊ + ` + +> Options + + `␊ + [␊ + {␊ + "withDash": true␊ + }␊ + ]␊ + ` + +> Error 1/1 + + `␊ + > 1 | ␊ + | ^^^^^^ Prefer \`utf-8\` over \`utf8\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Replace \`utf8\` with \`utf-8\`.␊ + 1 | ␊ + ` + +## invalid(12): + +> Input + + `␊ + 1 | ␊ + ` + +> Options + + `␊ + [␊ + {␊ + "withDash": true␊ + }␊ + ]␊ + ` + +> Error 1/1 + + `␊ + > 1 | ␊ + | ^^^^^^ Prefer \`utf-8\` over \`utf8\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Replace \`utf8\` with \`utf-8\`.␊ + 1 | ␊ + ` diff --git a/test/snapshots/text-encoding-identifier-case.js.snap b/test/snapshots/text-encoding-identifier-case.js.snap index 0e93a762c2..9780210a70 100644 Binary files a/test/snapshots/text-encoding-identifier-case.js.snap and b/test/snapshots/text-encoding-identifier-case.js.snap differ diff --git a/test/text-encoding-identifier-case.js b/test/text-encoding-identifier-case.js index 2b0930d659..7b50214065 100644 --- a/test/text-encoding-identifier-case.js +++ b/test/text-encoding-identifier-case.js @@ -3,6 +3,15 @@ import {getTester} from './utils/test.js'; const {test} = getTester(import.meta); test.snapshot({ + testerOptions: { + languageOptions: { + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, + }, valid: [ '`UTF-8`', '"utf8"', @@ -12,6 +21,8 @@ test.snapshot({ String.raw`"\u0055tf8"`, 'const ASCII = 1', 'const UTF8 = 1', + '', + '', ], invalid: [ '"UTF-8"', @@ -33,10 +44,14 @@ test.snapshot({ 'await fs.readFile(file, "UTF-8",)', 'fs.promises.readFile(file, "UTF-8",)', 'whatever.readFile(file, "UTF-8",)', + '', + '', + '', + '', ], }); -// JSX +// `withDash` option test.snapshot({ testerOptions: { languageOptions: { @@ -48,13 +63,26 @@ test.snapshot({ }, }, valid: [ - '', - '', - ], + '`Utf-8`;', + '"utf-8";', + '" Utf8 ";', + '\'utf-8\';', + 'const utf8 = 2;', + '', + '', + ].map(code => ({code, options: [{withDash: true}]})), invalid: [ - '', - '', - '', - '', - ], + '"UTF-8";', + '"UTF8";', + '"utf8";', + '\'utf8\';', + '"Utf8";', + '"ASCII";', + 'fs.readFile(file, "utf8",);', + 'whatever.readFile(file, "UTF8",)', + '', + '', + '', + '', + ].map(code => ({code, options: [{withDash: true}]})), });