@book000/eslint-config 1.8.55 → 1.8.57
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/.depcheckrc.json +7 -0
- package/.devcontainer/devcontainer.json +23 -0
- package/package.json +7 -1
- package/test.mjs +274 -0
package/.depcheckrc.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ESLint Config DevContainer",
|
|
3
|
+
"image": "mcr.microsoft.com/devcontainers/typescript-node:20-bullseye",
|
|
4
|
+
"features": {
|
|
5
|
+
"ghcr.io/devcontainers/features/node:1": {
|
|
6
|
+
"version": "20"
|
|
7
|
+
},
|
|
8
|
+
"ghcr.io/devcontainers/features/git:1": {}
|
|
9
|
+
},
|
|
10
|
+
"customizations": {
|
|
11
|
+
"vscode": {
|
|
12
|
+
"settings": {
|
|
13
|
+
"terminal.integrated.defaultProfile.linux": "bash"
|
|
14
|
+
},
|
|
15
|
+
"extensions": [
|
|
16
|
+
"dbaeumer.vscode-eslint",
|
|
17
|
+
"esbenp.prettier-vscode",
|
|
18
|
+
"GitHub.copilot"
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"postCreateCommand": "pnpm install"
|
|
23
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@book000/eslint-config",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.57",
|
|
4
4
|
"description": "ESLint config",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"eslint-config"
|
|
@@ -20,6 +20,9 @@
|
|
|
20
20
|
"@eslint/eslintrc": "3.3.1",
|
|
21
21
|
"@typescript-eslint/parser": "8.31.0",
|
|
22
22
|
"eslint-config-prettier": "10.1.2",
|
|
23
|
+
"eslint-plugin-import": "2.31.0",
|
|
24
|
+
"eslint-plugin-n": "17.17.0",
|
|
25
|
+
"eslint-plugin-promise": "7.2.1",
|
|
23
26
|
"eslint-plugin-unicorn": "58.0.0",
|
|
24
27
|
"globals": "16.0.0",
|
|
25
28
|
"typescript-eslint": "8.31.0"
|
|
@@ -27,5 +30,8 @@
|
|
|
27
30
|
"peerDependencies": {
|
|
28
31
|
"eslint": "9.25.1",
|
|
29
32
|
"eslint-config-standard": "17.1.0"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"test": "TEST_CLI=1 node --experimental-vm-modules test.mjs"
|
|
30
36
|
}
|
|
31
37
|
}
|
package/test.mjs
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
// Node.jsスクリプトでESLint flat config(index.mjs)の動作を検証するサンプル
|
|
2
|
+
import { exec } from "child_process";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
|
|
6
|
+
async function main() {
|
|
7
|
+
const testCases = [
|
|
8
|
+
{
|
|
9
|
+
name: "prefer-top-level-await: トップレベルawaitはOK",
|
|
10
|
+
code: "await Promise.resolve(1);",
|
|
11
|
+
shouldError: false,
|
|
12
|
+
rules: ["unicorn/prefer-top-level-await"],
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
name: "prevent-abbreviations: dev, prodなど省略形はOK",
|
|
16
|
+
code: "const dev = true; const prod = false;",
|
|
17
|
+
shouldError: false,
|
|
18
|
+
rules: ["unicorn/prevent-abbreviations"],
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: "no-null: nullの使用はOK",
|
|
22
|
+
code: "const a = null;",
|
|
23
|
+
shouldError: false,
|
|
24
|
+
rules: ["unicorn/no-null"],
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: "no-use-before-define: 定義前の変数使用はエラー",
|
|
28
|
+
code: "console.log(a); const a = 1;",
|
|
29
|
+
shouldError: true,
|
|
30
|
+
rules: ["@typescript-eslint/no-use-before-define"],
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: "no-use-before-define: 定義後の変数使用はOK",
|
|
34
|
+
code: "const a = 1; console.log(a);",
|
|
35
|
+
shouldError: false,
|
|
36
|
+
rules: ["@typescript-eslint/no-use-before-define"],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "no-explicit-any: any型の使用はOK",
|
|
40
|
+
code: "let a: any = 1; a = 'str';",
|
|
41
|
+
shouldError: false,
|
|
42
|
+
rules: ["@typescript-eslint/no-explicit-any"],
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "no-unsafe-assignment: unsafeな代入はOK",
|
|
46
|
+
code: "function f(x: any) { const y: number = x; }",
|
|
47
|
+
shouldError: false,
|
|
48
|
+
rules: ["@typescript-eslint/no-unsafe-assignment"],
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: "no-unsafe-argument: unsafeな引数はOK",
|
|
52
|
+
code: "function f(x: number) {} f(({} as any));",
|
|
53
|
+
shouldError: false,
|
|
54
|
+
rules: ["@typescript-eslint/no-unsafe-argument"],
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: "restrict-template-expressions: number, boolean, any, nullishはOK",
|
|
58
|
+
code: "const n = 1; const b = true; const a: any = 2; const u = undefined; `${n}${b}${a}${u}`;",
|
|
59
|
+
shouldError: false,
|
|
60
|
+
rules: ["@typescript-eslint/restrict-template-expressions"],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: "restrict-template-expressions: numberはOK",
|
|
64
|
+
code: "const n = 42; `${n}`;",
|
|
65
|
+
shouldError: false,
|
|
66
|
+
rules: ["@typescript-eslint/restrict-template-expressions"],
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: "restrict-template-expressions: booleanはOK",
|
|
70
|
+
code: "const b = true; `${b}`;",
|
|
71
|
+
shouldError: false,
|
|
72
|
+
rules: ["@typescript-eslint/restrict-template-expressions"],
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: "restrict-template-expressions: nullはOK",
|
|
76
|
+
code: "const n = null; `${n}`;",
|
|
77
|
+
shouldError: false,
|
|
78
|
+
rules: ["@typescript-eslint/restrict-template-expressions"],
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: "restrict-template-expressions: undefinedはOK",
|
|
82
|
+
code: "const u = undefined; `${u}`;",
|
|
83
|
+
shouldError: false,
|
|
84
|
+
rules: ["@typescript-eslint/restrict-template-expressions"],
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: "restrict-template-expressions: anyはOK",
|
|
88
|
+
code: "const a: any = 123; `${a}`;",
|
|
89
|
+
shouldError: false,
|
|
90
|
+
rules: ["@typescript-eslint/restrict-template-expressions"],
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "restrict-template-expressions: objectはエラー",
|
|
94
|
+
code: "const o = {}; `${o}`;",
|
|
95
|
+
shouldError: true,
|
|
96
|
+
rules: ["@typescript-eslint/restrict-template-expressions"],
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: "restrict-template-expressions: objectはエラー(詳細)",
|
|
100
|
+
code: "const o = { foo: 1 }; `${o}`;",
|
|
101
|
+
shouldError: true,
|
|
102
|
+
rules: ["@typescript-eslint/restrict-template-expressions"],
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: "no-floating-promises: 通常のPromiseはエラー",
|
|
106
|
+
code: "Promise.resolve(1);",
|
|
107
|
+
shouldError: true,
|
|
108
|
+
rules: ["@typescript-eslint/no-floating-promises"],
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: "no-floating-promises: void演算子はOK",
|
|
112
|
+
code: "void Promise.resolve(1);",
|
|
113
|
+
shouldError: false,
|
|
114
|
+
rules: ["@typescript-eslint/no-floating-promises"],
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: "no-floating-promises: void演算子なしはエラー",
|
|
118
|
+
code: "Promise.resolve(1);",
|
|
119
|
+
shouldError: true,
|
|
120
|
+
rules: ["@typescript-eslint/no-floating-promises"],
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: "no-floating-promises: 即時関数でawaitなしはエラー",
|
|
124
|
+
code: "(async () => { Promise.resolve(1); })();",
|
|
125
|
+
shouldError: true,
|
|
126
|
+
rules: ["@typescript-eslint/no-floating-promises"],
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: "no-floating-promises: 即時関数はOK",
|
|
130
|
+
code: "(async () => { await Promise.resolve(1); })();",
|
|
131
|
+
shouldError: false,
|
|
132
|
+
rules: ["@typescript-eslint/no-floating-promises"],
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: "no-unnecessary-condition: 不要な条件式はエラー",
|
|
136
|
+
code: "if (true) { console.log(1); }",
|
|
137
|
+
shouldError: true,
|
|
138
|
+
rules: ["@typescript-eslint/no-unnecessary-condition"],
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: "no-unnecessary-condition: 無限ループ条件はOK",
|
|
142
|
+
code: "while (true) { break; }",
|
|
143
|
+
shouldError: false,
|
|
144
|
+
rules: ["@typescript-eslint/no-unnecessary-condition"],
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
name: "no-extraneous-class: 名前空間的クラスはOK",
|
|
148
|
+
code: "class Namespace {}",
|
|
149
|
+
shouldError: false,
|
|
150
|
+
rules: ["@typescript-eslint/no-extraneous-class"],
|
|
151
|
+
},
|
|
152
|
+
];
|
|
153
|
+
|
|
154
|
+
// テスト用一時ファイルをsrc/配下に作成することで、flat configのfiles: ["**/*.ts"]に確実にマッチさせる
|
|
155
|
+
const tmpDir = path.join(process.cwd(), "src", "__tmp__cli");
|
|
156
|
+
if (!fs.existsSync(path.join(process.cwd(), "src")))
|
|
157
|
+
fs.mkdirSync(path.join(process.cwd(), "src"));
|
|
158
|
+
if (!fs.existsSync(tmpDir)) fs.mkdirSync(tmpDir);
|
|
159
|
+
|
|
160
|
+
// テスト用tsconfig.jsonを作成
|
|
161
|
+
const tsconfigPath = path.join(process.cwd(), "tsconfig.json");
|
|
162
|
+
fs.writeFileSync(
|
|
163
|
+
tsconfigPath,
|
|
164
|
+
JSON.stringify(
|
|
165
|
+
{
|
|
166
|
+
compilerOptions: {
|
|
167
|
+
target: "ES2020",
|
|
168
|
+
module: "ESNext",
|
|
169
|
+
moduleResolution: "Node",
|
|
170
|
+
strict: true,
|
|
171
|
+
esModuleInterop: true,
|
|
172
|
+
skipLibCheck: true,
|
|
173
|
+
},
|
|
174
|
+
include: ["src/**/*.ts", "src/**/*.tsx"],
|
|
175
|
+
},
|
|
176
|
+
null,
|
|
177
|
+
2
|
|
178
|
+
)
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
// テスト用にflat configファイル名をeslint.config.mjsに一時コピー
|
|
182
|
+
const flatConfigPath = path.join(process.cwd(), "eslint.config.mjs");
|
|
183
|
+
fs.copyFileSync(path.join(process.cwd(), "index.mjs"), flatConfigPath);
|
|
184
|
+
|
|
185
|
+
// 並列実行用のPromise配列
|
|
186
|
+
const promises = testCases.map((testCase, i) => {
|
|
187
|
+
return new Promise((resolve) => {
|
|
188
|
+
const { name, code, shouldError, rules } = testCase;
|
|
189
|
+
const kebabName = `test-${i}.ts`;
|
|
190
|
+
const tmpFilePath = path.join(tmpDir, kebabName);
|
|
191
|
+
fs.writeFileSync(tmpFilePath, code);
|
|
192
|
+
exec(
|
|
193
|
+
`npx eslint --no-cache --ext .ts ${tmpFilePath}`,
|
|
194
|
+
(error, stdout, stderr) => {
|
|
195
|
+
const output = (stdout || "") + (stderr || "");
|
|
196
|
+
// テスト項目ごとに対象ルールのみを判定
|
|
197
|
+
const errorLines = output
|
|
198
|
+
.split("\n")
|
|
199
|
+
.filter((line) => line.match(/error/));
|
|
200
|
+
let errorCount = 0;
|
|
201
|
+
let ignoredErrors = [];
|
|
202
|
+
const relevantErrors = [];
|
|
203
|
+
for (const line of errorLines) {
|
|
204
|
+
const ruleMatch = line.match(/\s([\w@\-/]+)$/);
|
|
205
|
+
const rule = ruleMatch ? ruleMatch[1] : null;
|
|
206
|
+
if (rule && rules.includes(rule)) {
|
|
207
|
+
errorCount = 1;
|
|
208
|
+
relevantErrors.push(line);
|
|
209
|
+
} else {
|
|
210
|
+
ignoredErrors.push(line);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
fs.unlinkSync(tmpFilePath);
|
|
214
|
+
resolve({
|
|
215
|
+
name,
|
|
216
|
+
shouldError,
|
|
217
|
+
errorCount,
|
|
218
|
+
output,
|
|
219
|
+
relevantErrors,
|
|
220
|
+
ignoredErrors,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
);
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
const results = await Promise.all(promises);
|
|
228
|
+
let pass = 0,
|
|
229
|
+
fail = 0;
|
|
230
|
+
for (const {
|
|
231
|
+
name,
|
|
232
|
+
shouldError,
|
|
233
|
+
errorCount,
|
|
234
|
+
output,
|
|
235
|
+
relevantErrors,
|
|
236
|
+
ignoredErrors,
|
|
237
|
+
} of results) {
|
|
238
|
+
if (shouldError ? errorCount > 0 : errorCount === 0) {
|
|
239
|
+
console.log(`✅ ${name}`);
|
|
240
|
+
pass++;
|
|
241
|
+
} else {
|
|
242
|
+
console.error(
|
|
243
|
+
`❌ ${name} (期待: ${shouldError ? "エラー" : "OK"}, 実際: ${
|
|
244
|
+
errorCount > 0 ? "エラー" : "OK"
|
|
245
|
+
})`
|
|
246
|
+
);
|
|
247
|
+
fail++;
|
|
248
|
+
}
|
|
249
|
+
if (relevantErrors && relevantErrors.length > 0) {
|
|
250
|
+
console.error(` 対象ルールのエラー:`);
|
|
251
|
+
relevantErrors.forEach((e) => console.error(` ${e}`));
|
|
252
|
+
}
|
|
253
|
+
if (ignoredErrors && ignoredErrors.length > 0) {
|
|
254
|
+
console.warn(` 無視したエラー:`);
|
|
255
|
+
ignoredErrors.forEach((e) => console.warn(` ${e}`));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
fs.unlinkSync(flatConfigPath);
|
|
260
|
+
fs.rmSync(tmpDir, { recursive: true });
|
|
261
|
+
fs.unlinkSync(tsconfigPath);
|
|
262
|
+
console.log("\n--- サマリ ---");
|
|
263
|
+
console.log(`成功: ${pass} / 失敗: ${fail} / 合計: ${testCases.length}`);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (process.env.CI || process.env.TEST_CLI) {
|
|
267
|
+
await main();
|
|
268
|
+
process.exit(0);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
main().catch((e) => {
|
|
272
|
+
console.error(e);
|
|
273
|
+
process.exit(1);
|
|
274
|
+
});
|