Skip to content

Commit

Permalink
Add text-encoding-identifier-case rule (#1718)
Browse files Browse the repository at this point in the history
  • Loading branch information
fisker committed Feb 9, 2022
1 parent f32e3a7 commit 4370602
Show file tree
Hide file tree
Showing 8 changed files with 225 additions and 0 deletions.
1 change: 1 addition & 0 deletions configs/recommended.js
Expand Up @@ -114,6 +114,7 @@ module.exports = {
'unicorn/require-post-message-target-origin': 'off',
'unicorn/string-content': 'off',
'unicorn/template-indent': 'warn',
'unicorn/text-encoding-identifier-case': 'error',
'unicorn/throw-new-error': 'error',
},
};
39 changes: 39 additions & 0 deletions docs/rules/text-encoding-identifier-case.md
@@ -0,0 +1,39 @@
# Enforce consistent case for text encoding identifiers

<!-- Do not manually modify RULE_NOTICE part. Run: `npm run generate-rule-notices` -->
<!-- RULE_NOTICE -->
*This rule is part of the [recommended](https://github.com/sindresorhus/eslint-plugin-unicorn#recommended-config) config.*

💡 *This rule provides [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).*
<!-- /RULE_NOTICE -->

- Enforce `'utf8'` for [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoding.
- Enforce `'ascii'` for [ASCII](https://en.wikipedia.org/wiki/ASCII) encoding.

## Fail

```js
await fs.readFile(file, 'UTF-8');
```

```js
await fs.readFile(file, 'ASCII');
```

```js
const string = buffer.toString('utf-8');
```

## Pass

```js
await fs.readFile(file, 'utf8');
```

```js
await fs.readFile(file, 'ascii');
```

```js
const string = buffer.toString('utf8');
```
1 change: 1 addition & 0 deletions readme.md
Expand Up @@ -150,6 +150,7 @@ Each rule has emojis denoting:
| [require-post-message-target-origin](docs/rules/require-post-message-target-origin.md) | Enforce using the `targetOrigin` argument with `window.postMessage()`. | | | 💡 |
| [string-content](docs/rules/string-content.md) | Enforce better string content. | | 🔧 | 💡 |
| [template-indent](docs/rules/template-indent.md) | Fix whitespace-insensitive template indentation. || 🔧 | |
| [text-encoding-identifier-case](docs/rules/text-encoding-identifier-case.md) | Enforce consistent case for text encoding identifiers. || | 💡 |
| [throw-new-error](docs/rules/throw-new-error.md) | Require `new` when throwing an error. || 🔧 | |
<!-- /RULES_TABLE -->

Expand Down
69 changes: 69 additions & 0 deletions rules/text-encoding-identifier-case.js
@@ -0,0 +1,69 @@
'use strict';
const {replaceStringLiteral} = require('./fix/index.js');

const MESSAGE_ID_ERROR = 'text-encoding-identifier/error';
const MESSAGE_ID_SUGGESTION = 'text-encoding-identifier/suggestion';
const messages = {
[MESSAGE_ID_ERROR]: 'Prefer `{{replacement}}` over `{{value}}`.',
[MESSAGE_ID_SUGGESTION]: 'Replace `{{value}}` with `{{replacement}}`.',
};

const getReplacement = encoding => {
switch (encoding.toLowerCase()) {
case 'utf8':
case 'utf-8':
return 'utf8';
case 'ascii':
return 'ascii';
// No default
}
};

/** @param {import('eslint').Rule.RuleContext} context */
const create = () => ({
Literal(node) {
if (typeof node.value !== 'string') {
return;
}

const {raw} = node;
const value = raw.slice(1, -1);

const replacement = getReplacement(value);
if (!replacement || replacement === value) {
return;
}

const messageData = {
value,
replacement,
};

return {
node,
messageId: MESSAGE_ID_ERROR,
data: messageData,
suggest: [
{
messageId: MESSAGE_ID_SUGGESTION,
data: messageData,
/** @param {import('eslint').Rule.RuleFixer} fixer */
fix: fixer => replaceStringLiteral(fixer, node, replacement),
},
],
};
},
});

/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
create,
meta: {
type: 'suggestion',
docs: {
description: 'Enforce consistent case for text encoding identifiers.',
},
hasSuggestions: true,
messages,
},
};
2 changes: 2 additions & 0 deletions test/run-rules-on-codebase/lint.mjs
Expand Up @@ -35,6 +35,8 @@ const eslint = new ESLint({
'unicorn/prefer-string-replace-all': 'off',
'unicorn/prefer-top-level-await': 'off',
'unicorn/prefer-at': 'off',
// TODO: Turn this on when `xo` updated `eslint-plugin-unicorn`
'unicorn/text-encoding-identifier-case': 'off',
},
overrides: [
{
Expand Down
89 changes: 89 additions & 0 deletions test/snapshots/text-encoding-identifier-case.mjs.md
@@ -0,0 +1,89 @@
# Snapshot report for `test/text-encoding-identifier-case.mjs`

The actual snapshot is saved in `text-encoding-identifier-case.mjs.snap`.

Generated by [AVA](https://avajs.dev).

## Invalid #1
1 | "UTF-8"

> Error 1/1
`␊
> 1 | "UTF-8"␊
| ^^^^^^^ Prefer \`utf8\` over \`UTF-8\`.␊
--------------------------------------------------------------------------------␊
Suggestion 1/1: Replace \`UTF-8\` with \`utf8\`.␊
1 | "utf8"␊
`

## Invalid #2
1 | "utf-8"

> Error 1/1
`␊
> 1 | "utf-8"␊
| ^^^^^^^ Prefer \`utf8\` over \`utf-8\`.␊
--------------------------------------------------------------------------------␊
Suggestion 1/1: Replace \`utf-8\` with \`utf8\`.␊
1 | "utf8"␊
`

## Invalid #3
1 | 'utf-8'

> Error 1/1
`␊
> 1 | 'utf-8'␊
| ^^^^^^^ Prefer \`utf8\` over \`utf-8\`.␊
--------------------------------------------------------------------------------␊
Suggestion 1/1: Replace \`utf-8\` with \`utf8\`.␊
1 | 'utf8'␊
`

## Invalid #4
1 | "Utf8"

> Error 1/1
`␊
> 1 | "Utf8"␊
| ^^^^^^ Prefer \`utf8\` over \`Utf8\`.␊
--------------------------------------------------------------------------------␊
Suggestion 1/1: Replace \`Utf8\` with \`utf8\`.␊
1 | "utf8"␊
`

## Invalid #5
1 | "ASCII"

> Error 1/1
`␊
> 1 | "ASCII"␊
| ^^^^^^^ Prefer \`ascii\` over \`ASCII\`.␊
--------------------------------------------------------------------------------␊
Suggestion 1/1: Replace \`ASCII\` with \`ascii\`.␊
1 | "ascii"␊
`

## Invalid #6
1 | await fs.readFile(file, "UTF-8",)

> Error 1/1
`␊
> 1 | await fs.readFile(file, "UTF-8",)␊
| ^^^^^^^ Prefer \`utf8\` over \`UTF-8\`.␊
--------------------------------------------------------------------------------␊
Suggestion 1/1: Replace \`UTF-8\` with \`utf8\`.␊
1 | await fs.readFile(file, "utf8",)␊
`
Binary file not shown.
24 changes: 24 additions & 0 deletions test/text-encoding-identifier-case.mjs
@@ -0,0 +1,24 @@
import {getTester} from './utils/test.mjs';

const {test} = getTester(import.meta);

test.snapshot({
valid: [
'`UTF-8`',
'"utf8"',
'"utf+8"',
'" utf8 "',
'\'utf8\'',
'"\\u0055tf8"',
'const ASCII = 1',
'const UTF8 = 1',
],
invalid: [
'"UTF-8"',
'"utf-8"',
'\'utf-8\'',
'"Utf8"',
'"ASCII"',
'await fs.readFile(file, "UTF-8",)',
],
});

0 comments on commit 4370602

Please sign in to comment.