eslint-config-typed 7.2.0 → 7.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -43,6 +43,7 @@ A comprehensive ESLint configuration package with strongly-typed rule definition
43
43
  - [Override Specific Rules](#override-specific-rules)
44
44
  - [Use RulesOptions Types](#use-rulesoptions-types)
45
45
  - [Target Specific Files](#target-specific-files)
46
+ - [Why Some Rules Are On or Off](#why-some-rules-are-on-or-off)
46
47
  - [Troubleshooting](#troubleshooting)
47
48
  - [Common Issues](#common-issues)
48
49
  - [1. ESLint can't find tsconfig.json](#1-eslint-cant-find-tsconfigjson)
@@ -900,6 +901,10 @@ export default [
900
901
  ] satisfies readonly FlatConfig[];
901
902
  ```
902
903
 
904
+ ## Why Some Rules Are On or Off
905
+
906
+ [`docs/rule-rationale.md`](https://github.com/noshiro-pf/mono/blob/main/libs/eslint-config-typed/docs/rule-rationale.md) (in Japanese) explains rules whose setting does not explain itself, such as why loops prefer `for-of` while loop statements stay allowed, and why `reduce` is allowed but only with an initial value. It also says how to fix a report from each rule.
907
+
903
908
  ## Troubleshooting
904
909
 
905
910
  ### Common Issues
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-config-typed",
3
- "version": "7.2.0",
3
+ "version": "7.2.1",
4
4
  "private": false,
5
5
  "keywords": [
6
6
  "typescript"
@@ -90,10 +90,10 @@
90
90
  "@vitest/ui": "5.0.5",
91
91
  "cspell": "10.4.0",
92
92
  "dedent": "1.7.2",
93
- "eslint-config-typed": "7.2.0",
94
- "eslint-plugin-ts-data-forge": "0.10.5",
93
+ "eslint-config-typed": "7.2.1",
94
+ "eslint-plugin-ts-data-forge": "0.10.6",
95
95
  "eslint-plugin-ts-fortress": "1.3.11",
96
- "eslint-plugin-ts-type-forge": "0.8.15",
96
+ "eslint-plugin-ts-type-forge": "0.8.16",
97
97
  "immer": "11.2.7",
98
98
  "jiti": "2.7.0",
99
99
  "json-schema": "0.4.0",
@@ -0,0 +1,148 @@
1
+ import { ESLint, type Linter } from 'eslint';
2
+ import * as fs from 'node:fs/promises';
3
+ import * as path from 'node:path';
4
+ import { Arr, isRecord } from 'ts-data-forge';
5
+ import type { ReadonlyRecord } from 'ts-type-forge';
6
+ import { eslintConfigForTypeScript } from '../configs/index.mjs';
7
+ import { eslintTsDataForgeRules } from './eslint-ts-data-forge-rules.mjs';
8
+
9
+ // `docs/rule-rationale.md` explains why a rule is on or off in the preset; a
10
+ // table row, or a setting the prose relies on, that no longer matches what the
11
+ // preset configures says the opposite of what the package does, and nothing
12
+ // else would notice.
13
+ describe('docs/rule-rationale.md', () => {
14
+ const packageRoot = path.resolve(import.meta.dirname, '../..');
15
+
16
+ /** The config the preset resolves to for an ordinary source file. */
17
+ const presetConfig = async (): Promise<
18
+ Readonly<{
19
+ rules: ReadonlyRecord<string, unknown>;
20
+ linterOptions: unknown;
21
+ }>
22
+ > => {
23
+ const eslint = new ESLint({
24
+ cwd: packageRoot,
25
+ overrideConfigFile: true,
26
+ overrideConfig: Array.from(
27
+ // eslint-disable-next-line total-functions/no-unsafe-type-assertion -- the preset's flat config, which ESLint validates
28
+ eslintConfigForTypeScript({
29
+ tsconfigFileName: 'tsconfig.json',
30
+ tsconfigRootDir: packageRoot,
31
+ packageDirs: [packageRoot],
32
+ }) as readonly Linter.Config[],
33
+ ),
34
+ });
35
+
36
+ const config: unknown = await eslint.calculateConfigForFile(
37
+ path.join(packageRoot, 'src/entry-point.mts'),
38
+ );
39
+
40
+ assert.isTrue(isRecord(config));
41
+
42
+ const { rules, linterOptions } = config;
43
+
44
+ assert.isTrue(isRecord(rules));
45
+
46
+ return { rules, linterOptions };
47
+ };
48
+
49
+ test('every table row names the severity the preset configures', async () => {
50
+ const [doc, { rules }] = await Promise.all([
51
+ fs.readFile(path.join(packageRoot, 'docs/rule-rationale.md'), 'utf8'),
52
+ presetConfig(),
53
+ ]);
54
+
55
+ // Every body row of every table is read; one this pattern does not parse
56
+ // fails instead of going unchecked.
57
+ const rows = doc
58
+ .split('\n')
59
+ .filter(
60
+ (line) =>
61
+ line.trimStart().startsWith('|') &&
62
+ !line.includes('---') &&
63
+ !line.includes('ルール'),
64
+ );
65
+
66
+ expect(rows.length).toBeGreaterThan(0);
67
+
68
+ const parsed = rows.map((row) => ({
69
+ row,
70
+ groups: rowPattern.exec(row)?.groups,
71
+ }));
72
+
73
+ assert.deepStrictEqual(
74
+ parsed.filter(({ groups }) => groups === undefined).map(({ row }) => row),
75
+ [],
76
+ );
77
+
78
+ for (const { groups } of parsed) {
79
+ const rule = groups?.['rule'];
80
+
81
+ assert.isDefined(rule);
82
+
83
+ const severity = groups?.['severity'];
84
+
85
+ expect(severityOf(rules[rule]), `${rule} in the preset`).toBe(severity);
86
+ }
87
+ });
88
+
89
+ test('the settings the prose relies on', async () => {
90
+ const { rules, linterOptions } = await presetConfig();
91
+
92
+ // A for-loop variable is reported unless it is named `mut_…`.
93
+ const noLet = rules['functional/no-let'];
94
+
95
+ assert.isTrue(Arr.isArray(noLet));
96
+
97
+ const option = noLet[1];
98
+
99
+ assert.isTrue(isRecord(option));
100
+
101
+ expect(option['allowInForLoopInit']).toBe(false);
102
+
103
+ expect(option['ignoreIdentifierPattern']).toContain('^mut_');
104
+
105
+ // A disable comment naming a rule that did not report is itself reported.
106
+ assert.isTrue(isRecord(linterOptions));
107
+
108
+ // ESLint resolves `true` to 'warn' and `false` to 'off'.
109
+ expect(['warn', 'error']).toContain(
110
+ severityOf(linterOptions['reportUnusedDisableDirectives']),
111
+ );
112
+
113
+ // `prefer-range-for-loop` is not in the preset; the prose says it has to
114
+ // be added.
115
+ expect(rules['ts-data-forge/prefer-range-for-loop']).toBeUndefined();
116
+ });
117
+
118
+ test('eslintTsDataForgeRules turns on prefer-range-for-loop', () => {
119
+ expect(
120
+ severityOf(eslintTsDataForgeRules['ts-data-forge/prefer-range-for-loop']),
121
+ ).toBe('error');
122
+ });
123
+ });
124
+
125
+ /** A table body row: `` | `<rule>` | `<severity>` | ``. */
126
+ const rowPattern = /^\| `(?<rule>[^`]+)` +\| `(?<severity>[^`]+)` +\|$/u;
127
+
128
+ /** `'off'`, `'warn'` or `'error'` for a rule entry, whatever its spelling. */
129
+ const severityOf = (entry: unknown): string | undefined => {
130
+ const level = Arr.isArray(entry) ? entry[0] : entry;
131
+
132
+ switch (level) {
133
+ case 0:
134
+ case 'off':
135
+ return 'off';
136
+
137
+ case 1:
138
+ case 'warn':
139
+ return 'warn';
140
+
141
+ case 2:
142
+ case 'error':
143
+ return 'error';
144
+
145
+ default:
146
+ return undefined;
147
+ }
148
+ };