Skip to content

Commit 72262fb

Browse files
Yanis Bensonsindresorhus
authored andcommitted
Alphabetically sort the rules and enforce that (#260)
1 parent 7544cf6 commit 72262fb

File tree

4 files changed

+117
-65
lines changed

4 files changed

+117
-65
lines changed

docs/new-rule.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Use the [`astexplorer` site](https://astexplorer.net) with the `espree` parser a
1717
- Go to the `rules` directory and duplicate the `no-hex-escape.js` file and rename it to the name of your rule. Then start implementing the new rule logic.
1818
- Add the correct [`meta.type`](https://eslint.org/docs/developer-guide/working-with-rules#rule-basics) to the rule.
1919
- Go to the `docs/rules` directory and duplicate the `no-hex-escape.md` file and rename it to the name of your rule. Then write some documentation.
20-
- Add the rule to the *bottom* of:
20+
- Add the rule in alphabetically sorted order to:
2121
- [The recommended config](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/352ba4a0291f9210ca5c8e2e61c7e3ad14028e77/index.js#L19)
2222
- [The recommended config in the readme](https://github.com/sindresorhus/eslint-plugin-unicorn/blame/352ba4a0291f9210ca5c8e2e61c7e3ad14028e77/readme.md#L35)
2323
- [The rule listing in the readme](https://github.com/sindresorhus/eslint-plugin-unicorn/blame/352ba4a0291f9210ca5c8e2e61c7e3ad14028e77/readme.md#L77)<br>

index.js

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,39 +18,39 @@ module.exports = {
1818
],
1919
rules: {
2020
'unicorn/catch-error-name': 'error',
21+
'unicorn/custom-error-definition': 'off',
22+
'unicorn/error-message': 'error',
23+
'unicorn/escape-case': 'error',
2124
'unicorn/explicit-length-check': 'error',
2225
'unicorn/filename-case': 'error',
26+
'unicorn/import-index': 'error',
27+
'unicorn/new-for-builtins': 'error',
2328
'unicorn/no-abusive-eslint-disable': 'error',
24-
'unicorn/no-process-exit': 'error',
25-
'unicorn/throw-new-error': 'error',
26-
'unicorn/number-literal-case': 'error',
27-
'unicorn/escape-case': 'error',
2829
'unicorn/no-array-instanceof': 'error',
29-
'unicorn/no-new-buffer': 'error',
30-
'unicorn/no-hex-escape': 'error',
31-
'unicorn/custom-error-definition': 'off',
32-
'unicorn/prefer-starts-ends-with': 'error',
33-
'unicorn/prefer-type-error': 'error',
30+
'unicorn/no-console-spaces': 'error',
3431
'unicorn/no-fn-reference-in-iterator': 'off',
35-
'unicorn/import-index': 'error',
36-
'unicorn/new-for-builtins': 'error',
37-
'unicorn/regex-shorthand': 'error',
38-
'unicorn/prefer-spread': 'error',
39-
'unicorn/error-message': 'error',
32+
'unicorn/no-for-loop': 'error',
33+
'unicorn/no-hex-escape': 'error',
34+
'unicorn/no-new-buffer': 'error',
35+
'unicorn/no-process-exit': 'error',
36+
'unicorn/no-unreadable-array-destructuring': 'error',
4037
'unicorn/no-unsafe-regex': 'off',
38+
'unicorn/no-unused-properties': 'off',
39+
'unicorn/no-zero-fractions': 'error',
40+
'unicorn/number-literal-case': 'error',
4141
'unicorn/prefer-add-event-listener': 'error',
4242
'unicorn/prefer-exponentiation-operator': 'error',
43-
'unicorn/no-console-spaces': 'error',
44-
'unicorn/no-unreadable-array-destructuring': 'error',
45-
'unicorn/no-unused-properties': 'off',
43+
'unicorn/prefer-includes': 'error',
4644
'unicorn/prefer-node-append': 'error',
47-
'unicorn/prefer-query-selector': 'error',
4845
'unicorn/prefer-node-remove': 'error',
46+
'unicorn/prefer-query-selector': 'error',
47+
'unicorn/prefer-spread': 'error',
48+
'unicorn/prefer-starts-ends-with': 'error',
4949
'unicorn/prefer-text-content': 'error',
50-
'unicorn/no-for-loop': 'error',
50+
'unicorn/prefer-type-error': 'error',
5151
'unicorn/prevent-abbreviations': 'error',
52-
'unicorn/prefer-includes': 'error',
53-
'unicorn/no-zero-fractions': 'error'
52+
'unicorn/regex-shorthand': 'error',
53+
'unicorn/throw-new-error': 'error'
5454
}
5555
}
5656
}

readme.md

Lines changed: 43 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -36,38 +36,39 @@ Configure it in `package.json`.
3636
],
3737
"rules": {
3838
"unicorn/catch-error-name": "error",
39+
"unicorn/custom-error-definition": "off",
40+
"unicorn/error-message": "error",
41+
"unicorn/escape-case": "error",
3942
"unicorn/explicit-length-check": "error",
4043
"unicorn/filename-case": "error",
41-
"unicorn/no-abusive-eslint-disable": "error",
42-
"unicorn/no-process-exit": "error",
43-
"unicorn/throw-new-error": "error",
44-
"unicorn/number-literal-case": "error",
45-
"unicorn/escape-case": "error",
46-
"unicorn/no-array-instanceof": "error",
47-
"unicorn/no-new-buffer": "error",
48-
"unicorn/no-hex-escape": "error",
49-
"unicorn/custom-error-definition": "off",
50-
"unicorn/prefer-starts-ends-with": "error",
51-
"unicorn/prefer-type-error": "error",
52-
"unicorn/no-fn-reference-in-iterator": "off",
5344
"unicorn/import-index": "error",
5445
"unicorn/new-for-builtins": "error",
55-
"unicorn/regex-shorthand": "error",
56-
"unicorn/prefer-spread": "error",
57-
"unicorn/error-message": "error",
58-
"unicorn/no-unsafe-regex": "off",
59-
"unicorn/prefer-add-event-listener": "error",
46+
"unicorn/no-abusive-eslint-disable": "error",
47+
"unicorn/no-array-instanceof": "error",
6048
"unicorn/no-console-spaces": "error",
49+
"unicorn/no-fn-reference-in-iterator": "off",
50+
"unicorn/no-for-loop": "error",
51+
"unicorn/no-hex-escape": "error",
52+
"unicorn/no-new-buffer": "error",
53+
"unicorn/no-process-exit": "error",
6154
"unicorn/no-unreadable-array-destructuring": "error",
55+
"unicorn/no-unsafe-regex": "off",
6256
"unicorn/no-unused-properties": "off",
57+
"unicorn/no-zero-fractions": "error",
58+
"unicorn/number-literal-case": "error",
59+
"unicorn/prefer-add-event-listener": "error",
60+
"unicorn/prefer-exponentiation-operator": "error",
61+
"unicorn/prefer-includes": "error",
6362
"unicorn/prefer-node-append": "error",
64-
"unicorn/prefer-query-selector": "error",
6563
"unicorn/prefer-node-remove": "error",
64+
"unicorn/prefer-query-selector": "error",
65+
"unicorn/prefer-spread": "error",
66+
"unicorn/prefer-starts-ends-with": "error",
6667
"unicorn/prefer-text-content": "error",
67-
"unicorn/no-for-loop": "error",
68+
"unicorn/prefer-type-error": "error",
6869
"unicorn/prevent-abbreviations": "error",
69-
"unicorn/prefer-includes": "error",
70-
"unicorn/no-zero-fractions": "error"
70+
"unicorn/regex-shorthand": "error",
71+
"unicorn/throw-new-error": "error"
7172
}
7273
}
7374
}
@@ -77,39 +78,39 @@ Configure it in `package.json`.
7778
## Rules
7879

7980
- [catch-error-name](docs/rules/catch-error-name.md) - Enforce a specific parameter name in catch clauses.
81+
- [custom-error-definition](docs/rules/custom-error-definition.md) - Enforce correct `Error` subclassing. *(fixable)*
82+
- [error-message](docs/rules/error-message.md) - Enforce passing a `message` value when throwing a built-in error.
83+
- [escape-case](docs/rules/escape-case.md) - Require escape sequences to use uppercase values. *(fixable)*
8084
- [explicit-length-check](docs/rules/explicit-length-check.md) - Enforce explicitly comparing the `length` property of a value. *(partly fixable)*
8185
- [filename-case](docs/rules/filename-case.md) - Enforce a case style for filenames.
86+
- [import-index](docs/rules/import-index.md) - Enforce importing index files with `.`. *(fixable)*
87+
- [new-for-builtins](docs/rules/new-for-builtins.md) - Enforce the use of `new` for all builtins, except `String`, `Number`, `Boolean` and `Symbol`. *(fixable)*
8288
- [no-abusive-eslint-disable](docs/rules/no-abusive-eslint-disable.md) - Enforce specifying rules to disable in `eslint-disable` comments.
83-
- [no-process-exit](docs/rules/no-process-exit.md) - Disallow `process.exit()`.
84-
- [throw-new-error](docs/rules/throw-new-error.md) - Require `new` when throwing an error. *(fixable)*
85-
- [number-literal-case](docs/rules/number-literal-case.md) - Enforce lowercase identifier and uppercase value for number literals. *(fixable)*
86-
- [escape-case](docs/rules/escape-case.md) - Require escape sequences to use uppercase values. *(fixable)*
8789
- [no-array-instanceof](docs/rules/no-array-instanceof.md) - Require `Array.isArray()` instead of `instanceof Array`. *(fixable)*
88-
- [no-new-buffer](docs/rules/no-new-buffer.md) - Enforce the use of `Buffer.from()` and `Buffer.alloc()` instead of the deprecated `new Buffer()`. *(fixable)*
89-
- [no-hex-escape](docs/rules/no-hex-escape.md) - Enforce the use of unicode escapes instead of hexadecimal escapes. *(fixable)*
90-
- [custom-error-definition](docs/rules/custom-error-definition.md) - Enforce correct `Error` subclassing. *(fixable)*
91-
- [prefer-starts-ends-with](docs/rules/prefer-starts-ends-with.md) - Prefer `String#startsWith` & `String#endsWith` over more complex alternatives.
92-
- [prefer-type-error](docs/rules/prefer-type-error.md) - Enforce throwing `TypeError` in type checking conditions. *(fixable)*
90+
- [no-console-spaces](docs/rules/no-console-spaces.md) - Do not use leading/trailing space between `console.log` parameters. *(fixable)*
9391
- [no-fn-reference-in-iterator](docs/rules/no-fn-reference-in-iterator.md) - Prevents passing a function reference directly to iterator methods. *(fixable)*
94-
- [import-index](docs/rules/import-index.md) - Enforce importing index files with `.`. *(fixable)*
95-
- [new-for-builtins](docs/rules/new-for-builtins.md) - Enforce the use of `new` for all builtins, except `String`, `Number`, `Boolean` and `Symbol`. *(fixable)*
96-
- [regex-shorthand](docs/rules/regex-shorthand.md) - Enforce the use of regex shorthands to improve readability. *(fixable)*
97-
- [prefer-spread](docs/rules/prefer-spread.md) - Prefer the spread operator over `Array.from()`. *(fixable)*
98-
- [error-message](docs/rules/error-message.md) - Enforce passing a `message` value when throwing a built-in error.
92+
- [no-for-loop](docs/rules/no-for-loop.md) - Do not use a `for` loop that can be replaced with a `for-of` loop. *(fixable)*
93+
- [no-hex-escape](docs/rules/no-hex-escape.md) - Enforce the use of unicode escapes instead of hexadecimal escapes. *(fixable)*
94+
- [no-new-buffer](docs/rules/no-new-buffer.md) - Enforce the use of `Buffer.from()` and `Buffer.alloc()` instead of the deprecated `new Buffer()`. *(fixable)*
95+
- [no-process-exit](docs/rules/no-process-exit.md) - Disallow `process.exit()`.
96+
- [no-unreadable-array-destructuring](docs/rules/no-unreadable-array-destructuring.md) - Disallow unreadable array destructuring.
9997
- [no-unsafe-regex](docs/rules/no-unsafe-regex.md) - Disallow unsafe regular expressions.
98+
- [no-unused-properties](docs/rules/no-unused-properties.md) - Disallow unused object properties.
99+
- [no-zero-fractions](docs/rules/no-zero-fractions.md) - Disallow number literals with zero fractions or dangling dots. *(fixable)*
100+
- [number-literal-case](docs/rules/number-literal-case.md) - Enforce lowercase identifier and uppercase value for number literals. *(fixable)*
100101
- [prefer-add-event-listener](docs/rules/prefer-add-event-listener.md) - Prefer `addEventListener` over `on`-functions. *(fixable)*
101102
- [prefer-exponentiation-operator](docs/rules/prefer-exponentiation-operator.md) - Prefer the exponentiation operator over `Math.pow()` *(fixable)*
102-
- [no-console-spaces](docs/rules/no-console-spaces.md) - Do not use leading/trailing space between `console.log` parameters. *(fixable)*
103-
- [no-unreadable-array-destructuring](docs/rules/no-unreadable-array-destructuring.md) - Disallow unreadable array destructuring.
104-
- [no-unused-properties](docs/rules/no-unused-properties.md) - Disallow unused object properties.
103+
- [prefer-includes](docs/rules/prefer-includes.md) - Prefer `.includes()` over `.indexOf()` when checking for existence or non-existence. *(fixable)*
105104
- [prefer-node-append](docs/rules/prefer-node-append.md) - Prefer `append` over `appendChild`. *(fixable)*
106-
- [prefer-query-selector](docs/rules/prefer-query-selector.md) - Prefer `querySelector` over `getElementById`, `querySelectorAll` over `getElementsByClassName` and `getElementsByTagName`. *(partly fixable)*
107105
- [prefer-node-remove](docs/rules/prefer-node-remove.md) - Prefer `remove` over `parentNode.removeChild` and `parentElement.removeChild`. *(fixable)*
106+
- [prefer-query-selector](docs/rules/prefer-query-selector.md) - Prefer `querySelector` over `getElementById`, `querySelectorAll` over `getElementsByClassName` and `getElementsByTagName`. *(partly fixable)*
107+
- [prefer-spread](docs/rules/prefer-spread.md) - Prefer the spread operator over `Array.from()`. *(fixable)*
108+
- [prefer-starts-ends-with](docs/rules/prefer-starts-ends-with.md) - Prefer `String#startsWith` & `String#endsWith` over more complex alternatives.
108109
- [prefer-text-content](docs/rules/prefer-text-content.md) - Prefer `textContent` over `innerText`. *(fixable)*
109-
- [no-for-loop](docs/rules/no-for-loop.md) - Do not use a `for` loop that can be replaced with a `for-of` loop. *(fixable)*
110+
- [prefer-type-error](docs/rules/prefer-type-error.md) - Enforce throwing `TypeError` in type checking conditions. *(fixable)*
110111
- [prevent-abbreviations](docs/rules/prevent-abbreviations.md) - Prevent abbreviations *(partly fixable)*
111-
- [prefer-includes](docs/rules/prefer-includes.md) - Prefer `.includes()` over `.indexOf()` when checking for existence or non-existence. *(fixable)*
112-
- [no-zero-fractions](docs/rules/no-zero-fractions.md) - Disallow number literals with zero fractions or dangling dots. *(fixable)*
112+
- [regex-shorthand](docs/rules/regex-shorthand.md) - Enforce the use of regex shorthands to improve readability. *(fixable)*
113+
- [throw-new-error](docs/rules/throw-new-error.md) - Require `new` when throwing an error. *(fixable)*
113114

114115

115116
## Recommended config

test/package.js

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,17 @@ test.before(async () => {
1111
ruleFiles = files.filter(file => path.extname(file) === '.js');
1212
});
1313

14-
test('Every rule is defined in index file', t => {
14+
const testSorted = (t, actualOrder, sourceName) => {
15+
const sortedOrder = actualOrder.slice(0).sort();
16+
17+
for (const [wantedIndex, name] of sortedOrder.entries()) {
18+
const actualIndex = actualOrder.indexOf(name);
19+
const whereMessage = (wantedIndex === 0) ? '' : `, after '${sortedOrder[wantedIndex - 1]}'`;
20+
t.is(actualIndex, wantedIndex, `${sourceName} should be alphabetically sorted, '${name}' should be placed at index ${wantedIndex}${whereMessage}`);
21+
}
22+
};
23+
24+
test('Every rule is defined in index file in alphabetical order', t => {
1525
for (const file of ruleFiles) {
1626
const name = path.basename(file, '.js');
1727
t.truthy(index.rules[name], `'${name}' is not exported in 'index.js'`);
@@ -24,6 +34,47 @@ test('Every rule is defined in index file', t => {
2434
'There are more exported rules than rule files.');
2535
t.is(Object.keys(index.configs.recommended.rules).length, ruleFiles.length,
2636
'There are more exported rules in the recommended config than rule files.');
37+
38+
testSorted(t, Object.keys(index.configs.recommended.rules), 'configs.recommended.rules');
39+
});
40+
41+
test('Every rule is defined in readme.md usage and list of rules in alphabetical order', async t => {
42+
const readme = await pify(fs.readFile)('readme.md', 'utf8');
43+
let usageRules;
44+
try {
45+
const usageRulesMatch = /## Usage.*?"rules": (\{.*?\})/ms.exec(readme);
46+
t.truthy(usageRulesMatch, 'List of rules should be defined in readme.md ## Usage');
47+
usageRules = JSON.parse(usageRulesMatch[1]);
48+
} catch (_) {}
49+
50+
t.truthy(usageRules, 'List of rules should be defined in readme.md ## Usage and be valid JSON');
51+
52+
const rulesMatch = /## Rules(.*?)## Recommended config/ms.exec(readme);
53+
t.truthy(rulesMatch, 'List of rules should be defined in readme.md in ## Rules before ## Recommended config');
54+
const rulesText = rulesMatch[1];
55+
const re = /- \[(.*?)\]\((.*?)\) - (.*)\n/gm;
56+
const rules = [];
57+
let match;
58+
do {
59+
match = re.exec(rulesText);
60+
if (match) {
61+
t.is(match[2], `docs/rules/${match[1]}.md`, `${match[1]} link to docs should be correct`);
62+
t.true(match[3].trim().length > 0, `${match[1]} should have description in readme.md ## Rules`);
63+
rules.push(match[1]);
64+
}
65+
} while (match);
66+
67+
for (const file of ruleFiles) {
68+
const name = path.basename(file, '.js');
69+
t.truthy(usageRules[`unicorn/${name}`], `'${name}' is not described in the readme.md ## Usage`);
70+
t.truthy(rules.includes(name), `'${name}' is not described in the readme.md ## Rules`);
71+
}
72+
73+
t.is(Object.keys(usageRules).length, ruleFiles.length, 'There are more rules in readme.md ## Usage than rule files.');
74+
t.is(Object.keys(rules).length, ruleFiles.length, 'There are more rules in readme.md ## Rules than rule files.');
75+
76+
testSorted(t, Object.keys(usageRules), 'readme.md ## Usage rules');
77+
testSorted(t, rules, 'readme.md ## Rules');
2778
});
2879

2980
test('Every rule has valid meta.type', t => {

0 commit comments

Comments
 (0)