@slashnephy/eslint-config 3.0.110 → 3.0.111

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
@@ -6,7 +6,7 @@
6
6
  ## Install
7
7
 
8
8
  ```console
9
- $ yarn add @slashnephy/eslint-config
9
+ pnpm add @slashnephy/eslint-config
10
10
  ```
11
11
 
12
12
  ## Usage (for Flat Configs)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slashnephy/eslint-config",
3
- "version": "3.0.110",
3
+ "version": "3.0.111",
4
4
  "keywords": [
5
5
  "eslint",
6
6
  "eslintconfig"
@@ -0,0 +1,10 @@
1
+ import { defineConfig, globalIgnores } from 'eslint/config';
2
+ import gitignore from 'eslint-config-flat-gitignore';
3
+ export const common = defineConfig(globalIgnores([
4
+ '**/.git/**',
5
+ '**/node_modules/**',
6
+ '**/.yarn/**',
7
+ '**/dist/**',
8
+ '**/.idea/**',
9
+ '**/pnpm-lock.yaml',
10
+ ]), gitignore({ name: 'eslint-config-flat-gitignore', strict: false }));
@@ -0,0 +1,43 @@
1
+ import graphqlPlugin from '@graphql-eslint/eslint-plugin';
2
+ import { defineConfig } from 'eslint/config';
3
+ export const graphql = defineConfig(
4
+ // GraphQL を解釈できるようにする
5
+ {
6
+ name: '@graphql-eslint/eslint-plugin (parser)',
7
+ files: ['**/*.{graphql,graphqls,gql}'],
8
+ languageOptions: {
9
+ parser: graphqlPlugin.parser,
10
+ },
11
+ plugins: {
12
+ // @ts-expect-error -- 一時的に型定義を無視
13
+ '@graphql-eslint': graphqlPlugin,
14
+ },
15
+ },
16
+ // コード中に含まれる GraphQL を解釈して lint できるようにする
17
+ {
18
+ name: '@graphql-eslint/eslint-plugin (code)',
19
+ files: ['**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}'],
20
+ processor: graphqlPlugin.processor,
21
+ },
22
+ // GraphQL schema
23
+ {
24
+ name: '@graphql-eslint/eslint-plugin (GraphQL schema)',
25
+ files: [
26
+ '**/schema.{graphql,graphqls,gql}',
27
+ // gqlgen
28
+ '**/graph/*.{graphql,graphqls,gql}',
29
+ ],
30
+ extends: [
31
+ graphqlPlugin.configs['flat/schema-recommended'],
32
+ graphqlPlugin.configs['flat/schema-relay'],
33
+ ],
34
+ },
35
+ // GraphQL operations
36
+ {
37
+ name: '@graphql-eslint/eslint-plugin (GraphQL operations)',
38
+ files: [
39
+ '**/documents/*.{graphql,graphqls,gql}',
40
+ '**/frontend/**/*.{graphql,graphqls,gql}',
41
+ ],
42
+ extends: [graphqlPlugin.configs['flat/operations-recommended']],
43
+ });
@@ -0,0 +1,246 @@
1
+ import eslint from '@eslint/js';
2
+ // @ts-expect-error 型定義ファイルがない
3
+ import eslintCommentsConfig from '@eslint-community/eslint-plugin-eslint-comments/configs';
4
+ import stylisticPlugin from '@stylistic/eslint-plugin';
5
+ import { defineConfig } from 'eslint/config';
6
+ import { importX } from 'eslint-plugin-import-x';
7
+ // @ts-expect-error 型定義ファイルがない
8
+ import promisePlugin from 'eslint-plugin-promise';
9
+ import unusedImportsPlugin from 'eslint-plugin-unused-imports';
10
+ import tseslint from 'typescript-eslint';
11
+ export const javaScript = defineConfig({
12
+ files: ['**/*.cjs'],
13
+ languageOptions: {
14
+ sourceType: 'commonjs',
15
+ },
16
+ }, {
17
+ files: ['**/*.{js,mjs,jsx}'],
18
+ languageOptions: {
19
+ sourceType: 'module',
20
+ },
21
+ }, {
22
+ name: '@eslint/js',
23
+ files: ['**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}'],
24
+ extends: [eslint.configs.recommended],
25
+ languageOptions: {
26
+ ecmaVersion: 'latest',
27
+ parser: tseslint.parser,
28
+ },
29
+ rules: {
30
+ // アロー関数を優先
31
+ 'prefer-arrow-callback': 'error',
32
+ // 関数宣言は function xxx() {} にする
33
+ 'func-style': ['error', 'declaration', { allowArrowFunctions: true }],
34
+ // 中括弧の省略を禁止
35
+ curly: 'error',
36
+ // テンプレート文字列を優先
37
+ 'prefer-template': 'error',
38
+ // == 比較 👉 === 比較
39
+ eqeqeq: 'error',
40
+ // *.js で 'use strict'; を強制
41
+ strict: ['error', 'global'],
42
+ // 特定の構文を禁止
43
+ 'no-restricted-syntax': [
44
+ 'error',
45
+ // 数値リテラル以外での Array#at() の使用を禁止
46
+ // https://qiita.com/printf_moriken/items/da03f55cb626617c1958
47
+ {
48
+ selector:
49
+ // eslint-disable-next-line @stylistic/quotes -- prettier と競合している
50
+ "CallExpression[callee.property.name='at']:not([arguments.0.type='Literal'],[arguments.0.type='UnaryExpression'][arguments.0.argument.type='Literal'])",
51
+ message: 'at method accepts only a literal argument',
52
+ },
53
+ ],
54
+ // foo["bar"] 👉 foo.bar
55
+ 'dot-notation': 'error',
56
+ // {foo: foo} 👉 {foo}
57
+ 'object-shorthand': ['error', 'always'],
58
+ // Array 系メソッドで return を強制
59
+ 'array-callback-return': ['error'],
60
+ // ループ内では await を禁止
61
+ 'no-await-in-loop': 'error',
62
+ // 操作が値に影響しない式を禁止
63
+ 'no-constant-binary-expression': 'error',
64
+ // コンストラクター内で return を禁止
65
+ 'no-constructor-return': 'error',
66
+ // 関数の返り値としての Promise executor を禁止
67
+ 'no-promise-executor-return': 'error',
68
+ // 自身との比較 (e.g. foo === foo) を禁止
69
+ 'no-self-compare': 'error',
70
+ // 非テンプレート文字列で ${foo} を禁止
71
+ // "Hello, ${name}" 👉 `Hello, ${name}`
72
+ 'no-template-curly-in-string': 'error',
73
+ // 更新されないループ条件を禁止
74
+ 'no-unmodified-loop-condition': 'error',
75
+ // 到達できないループを禁止
76
+ 'no-unreachable-loop': 'error',
77
+ // 未使用の private メンバーを禁止
78
+ 'no-unused-private-class-members': 'error',
79
+ // スレッドセーフで安全に更新されないコードを禁止
80
+ 'require-atomic-updates': 'error',
81
+ // ペアになっていない setter を禁止
82
+ 'accessor-pairs': 'error',
83
+ // キャメルケースに強制しない
84
+ camelcase: 'off',
85
+ // switch 文で default を強制しない
86
+ 'default-case': 'off',
87
+ // continue 文を許可
88
+ 'no-continue': 'off',
89
+ // _ で始まるメンバー名を許可
90
+ 'no-underscore-dangle': 'off',
91
+ // 自身より後に宣言されたメンバーの使用を許可
92
+ 'no-use-before-define': 'off',
93
+ // console.* の使用を許可
94
+ 'no-console': 'off',
95
+ // 深い三項演算子を許可
96
+ 'no-nested-ternary': 'off',
97
+ // i++ インクリメントを許可
98
+ 'no-plusplus': 'off',
99
+ // return の省略などを許可
100
+ 'consistent-return': 'off',
101
+ // void Promise を許可
102
+ 'no-void': 'off',
103
+ // 1 <= x < 10 を許可
104
+ yoda: [
105
+ 'error',
106
+ 'never',
107
+ {
108
+ exceptRange: true,
109
+ },
110
+ ],
111
+ // UTF-8 BOM を禁止
112
+ 'unicode-bom': ['error', 'never'],
113
+ },
114
+ }, {
115
+ name: '@stylistic/eslint-plugin',
116
+ files: ['**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}'],
117
+ extends: [
118
+ stylisticPlugin.configs.customize({
119
+ indent: 2,
120
+ quotes: 'single',
121
+ semi: false,
122
+ jsx: true,
123
+ arrowParens: true,
124
+ blockSpacing: true,
125
+ quoteProps: 'as-needed',
126
+ commaDangle: 'always-multiline',
127
+ braceStyle: '1tbs',
128
+ }),
129
+ ],
130
+ rules: {
131
+ // 最終行に改行を挿入
132
+ '@stylistic/eol-last': ['error', 'always'],
133
+ // 行末のスペースを禁止
134
+ '@stylistic/no-trailing-spaces': ['error'],
135
+ // 型名の前後のスペースを揃える
136
+ // e.g. const foo: string = 'bar'
137
+ '@stylistic/type-annotation-spacing': 'error',
138
+ // func () 👉 func()
139
+ '@stylistic/function-call-spacing': ['error', 'never'],
140
+ // 空行を挟む
141
+ '@stylistic/padding-line-between-statements': [
142
+ 'warn',
143
+ // return 前に空行
144
+ { blankLine: 'always', prev: '*', next: 'return' },
145
+ // ディレクティブ後に空行
146
+ { blankLine: 'always', prev: 'directive', next: '*' },
147
+ { blankLine: 'any', prev: 'directive', next: 'directive' },
148
+ ],
149
+ },
150
+ }, {
151
+ name: '@eslint-community/eslint-plugin-eslint-comments',
152
+ extends: [eslintCommentsConfig.recommended],
153
+ }, {
154
+ name: 'eslint-plugin-import-x',
155
+ files: ['**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}'],
156
+ extends: [
157
+ // @ts-expect-error -- languageOptions の型定義が不一致
158
+ importX.flatConfigs.recommended,
159
+ ],
160
+ settings: {
161
+ 'import-x/resolver': {
162
+ node: {
163
+ extensions: ['.js', '.cjs', '.mjs', '.jsx', '.ts', '.cts', '.mts', '.tsx', '.json'],
164
+ },
165
+ typescript: {
166
+ alwaysTryTypes: true,
167
+ },
168
+ },
169
+ },
170
+ rules: {
171
+ 'import-x/no-import-module-exports': 'off',
172
+ 'import-x/no-extraneous-dependencies': 'off',
173
+ // 循環 import を禁止
174
+ 'import-x/no-cycle': 'error',
175
+ // default export を優先しない
176
+ 'import-x/prefer-default-export': 'off',
177
+ // default export を禁止
178
+ 'import-x/no-default-export': 'error',
179
+ // import 順を並び替える
180
+ 'import-x/order': [
181
+ 'warn',
182
+ {
183
+ // 組み込み → 外部依存 → 内部依存 → object → type の順にする
184
+ groups: [
185
+ 'builtin',
186
+ 'external',
187
+ ['parent', 'sibling', 'index'],
188
+ 'object',
189
+ 'type',
190
+ 'unknown',
191
+ ],
192
+ // カテゴリー間に改行を入れる
193
+ 'newlines-between': 'always',
194
+ // 大文字小文字区別なしで ABC 順にする
195
+ alphabetize: {
196
+ order: 'asc',
197
+ caseInsensitive: true,
198
+ },
199
+ pathGroups: [
200
+ // **.css は最後に配置する
201
+ {
202
+ pattern: '**.css',
203
+ group: 'type',
204
+ position: 'after',
205
+ },
206
+ ],
207
+ // **.css が import 順最後ではないときに警告
208
+ warnOnUnassignedImports: true,
209
+ },
210
+ ],
211
+ },
212
+ }, {
213
+ name: 'eslint-plugin-unused-imports',
214
+ files: ['**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}'],
215
+ plugins: {
216
+ 'unused-imports': unusedImportsPlugin,
217
+ },
218
+ rules: {
219
+ // 不要 import 文を禁止
220
+ 'unused-imports/no-unused-imports': 'error',
221
+ // 不要な変数を禁止
222
+ 'no-unused-vars': 'off',
223
+ '@typescript-eslint/no-unused-vars': 'off',
224
+ 'unused-imports/no-unused-vars': [
225
+ 'warn',
226
+ // '_' で始まる変数を許可
227
+ {
228
+ vars: 'all',
229
+ varsIgnorePattern: '^_',
230
+ args: 'after-used',
231
+ argsIgnorePattern: '^_',
232
+ },
233
+ ],
234
+ },
235
+ }, {
236
+ name: 'eslint-plugin-promise',
237
+ files: ['**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}'],
238
+ extends: [promisePlugin.configs['flat/recommended']],
239
+ }, {
240
+ name: 'eslint-plugin-xss',
241
+ files: ['**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}'],
242
+ // TODO: eslint-plugin-xss が Flat Configs に対応したら移行する
243
+ // https://github.com/Rantanen/eslint-plugin-xss/issues/15
244
+ // extends: ['plugin:xss/recommended'],
245
+ extends: [],
246
+ });
@@ -0,0 +1,16 @@
1
+ import { defineConfig } from 'eslint/config';
2
+ import packageJsonPlugin from 'eslint-plugin-package-json';
3
+ export const packageJson = defineConfig({
4
+ name: 'eslint-plugin-package-json',
5
+ files: ['**/package.json'],
6
+ extends: [packageJsonPlugin.configs.recommended],
7
+ rules: {
8
+ 'package-json/require-description': 'off',
9
+ 'package-json/order-properties': [
10
+ 'error',
11
+ {
12
+ order: 'sort-package-json',
13
+ },
14
+ ],
15
+ },
16
+ });
@@ -0,0 +1,231 @@
1
+ import safeTypeScriptPlugin from '@susisu/eslint-plugin-safe-typescript';
2
+ import { defineConfig } from 'eslint/config';
3
+ import { importX } from 'eslint-plugin-import-x';
4
+ import tsdocPlugin from 'eslint-plugin-tsdoc';
5
+ import tseslint from 'typescript-eslint';
6
+ export const typeScript = defineConfig({
7
+ files: ['**/*.cts'],
8
+ languageOptions: {
9
+ sourceType: 'commonjs',
10
+ },
11
+ }, {
12
+ files: ['**/*.{ts,mts,tsx}'],
13
+ languageOptions: {
14
+ sourceType: 'module',
15
+ },
16
+ }, {
17
+ name: 'typescript-eslint',
18
+ files: ['**/*.{ts,cts,mts,tsx}'],
19
+ extends: [
20
+ tseslint.configs.recommendedTypeChecked,
21
+ tseslint.configs.stylisticTypeChecked,
22
+ ],
23
+ languageOptions: {
24
+ ecmaVersion: 'latest',
25
+ parser: tseslint.parser,
26
+ parserOptions: {
27
+ projectService: true,
28
+ // tsconfigRootDir は利用側で定義する必要がある
29
+ // tsconfigRootDir: import.meta.dirname,
30
+ },
31
+ },
32
+ rules: {
33
+ /**
34
+ * Automatically fixable は error にする
35
+ */
36
+ // interface 👉 type
37
+ '@typescript-eslint/consistent-type-definitions': ['error', 'type'],
38
+ // export type を優先
39
+ '@typescript-eslint/consistent-type-exports': [
40
+ 'error',
41
+ {
42
+ fixMixedExportsWithInlineTypeSpecifier: true,
43
+ },
44
+ ],
45
+ // import type を優先
46
+ '@typescript-eslint/consistent-type-imports': 'error',
47
+ // クラスのアクセス修飾子を強制
48
+ '@typescript-eslint/explicit-member-accessibility': 'error',
49
+ // export されているメンバーや public メンバーは型を明示させる
50
+ '@typescript-eslint/explicit-module-boundary-types': 'warn',
51
+ '@typescript-eslint/member-ordering': 'warn',
52
+ '@typescript-eslint/method-signature-style': ['warn', 'method'],
53
+ // 命名規則を強制
54
+ '@typescript-eslint/naming-convention': [
55
+ 'warn',
56
+ // デフォルトは camelCase
57
+ {
58
+ selector: ['default'],
59
+ format: ['strictCamelCase'],
60
+ },
61
+ // 型名 / 列挙型のメンバーは PascalCase
62
+ {
63
+ selector: ['typeLike', 'enumMember'],
64
+ format: ['StrictPascalCase'],
65
+ },
66
+ // 変数名は camelCase
67
+ {
68
+ selector: ['variableLike'],
69
+ format: ['strictCamelCase', 'StrictPascalCase'],
70
+ leadingUnderscore: 'allow',
71
+ },
72
+ // インポート名は camelCase / PascalCase
73
+ {
74
+ selector: ['import'],
75
+ format: ['camelCase', 'PascalCase'],
76
+ },
77
+ // export された定数は UPPER_CASE を許容
78
+ {
79
+ selector: ['variable'],
80
+ modifiers: ['const', 'global', 'exported'],
81
+ format: ['strictCamelCase', 'StrictPascalCase', 'UPPER_CASE'],
82
+ },
83
+ // プロパティーに snake_case / UPPER_CASE を許容
84
+ {
85
+ selector: ['property'],
86
+ format: [
87
+ 'strictCamelCase',
88
+ 'snake_case',
89
+ 'UPPER_CASE',
90
+ 'StrictPascalCase',
91
+ ],
92
+ },
93
+ // Boolean は特定のプレフィックスを強制
94
+ {
95
+ selector: ['variable'],
96
+ types: ['boolean'],
97
+ format: ['StrictPascalCase'],
98
+ prefix: [
99
+ 'is',
100
+ 'are',
101
+ 'was',
102
+ 'were',
103
+ 'should',
104
+ 'has',
105
+ 'can',
106
+ 'did',
107
+ 'will',
108
+ 'contains',
109
+ 'enable',
110
+ 'disable',
111
+ 'show',
112
+ 'hide',
113
+ ],
114
+ },
115
+ // プライベートメンバーは _ で始める
116
+ {
117
+ selector: ['memberLike'],
118
+ modifiers: ['private'],
119
+ format: ['strictCamelCase'],
120
+ leadingUnderscore: 'require',
121
+ },
122
+ // deconstruct で宣言された変数は許容
123
+ {
124
+ selector: ['variableLike'],
125
+ modifiers: ['destructured'],
126
+ format: null,
127
+ },
128
+ // オブジェクトのキーなど '' 付きの宣言は許容
129
+ {
130
+ selector: ['memberLike', 'property'],
131
+ modifiers: ['requiresQuotes'],
132
+ format: null,
133
+ },
134
+ ],
135
+ // void を式の値として禁止
136
+ '@typescript-eslint/no-confusing-void-expression': 'error',
137
+ // 重複した型定義を禁止
138
+ // boolean | false 👉 boolean
139
+ '@typescript-eslint/no-redundant-type-constituents': 'warn',
140
+ // require() を禁止
141
+ '@typescript-eslint/no-require-imports': 'warn',
142
+ '@typescript-eslint/no-unnecessary-qualifier': 'error',
143
+ '@typescript-eslint/no-useless-empty-export': 'error',
144
+ // パラメーターでのプロパティ宣言を強制
145
+ '@typescript-eslint/parameter-properties': [
146
+ 'warn',
147
+ {
148
+ allow: [
149
+ 'readonly',
150
+ 'private',
151
+ 'protected',
152
+ 'public',
153
+ 'private readonly',
154
+ 'protected readonly',
155
+ 'public readonly',
156
+ ],
157
+ prefer: 'parameter-property',
158
+ },
159
+ ],
160
+ '@typescript-eslint/prefer-enum-initializers': 'warn',
161
+ '@typescript-eslint/prefer-readonly': 'error',
162
+ '@typescript-eslint/prefer-regexp-exec': 'error',
163
+ // Promise<T> を返す関数では async のマークを強制
164
+ '@typescript-eslint/promise-function-async': 'error',
165
+ '@typescript-eslint/switch-exhaustiveness-check': 'error',
166
+ '@typescript-eslint/unbound-method': 'off',
167
+ // 過激なルールを無効化
168
+ '@typescript-eslint/no-unsafe-assignment': 'off',
169
+ '@typescript-eslint/no-unsafe-call': 'off',
170
+ '@typescript-eslint/no-unsafe-member-access': 'off',
171
+ '@typescript-eslint/no-unsafe-return': 'off',
172
+ '@typescript-eslint/no-unsafe-argument': 'off',
173
+ '@typescript-eslint/no-unsafe-enum-comparison': 'off',
174
+ '@typescript-eslint/restrict-template-expressions': 'off',
175
+ '@typescript-eslint/strict-boolean-expressions': 'off',
176
+ // enum のメンバーでビット演算を許可する
177
+ '@typescript-eslint/prefer-literal-enum-member': [
178
+ 'error',
179
+ {
180
+ allowBitwiseExpressions: true,
181
+ },
182
+ ],
183
+ // '1' + 2 を禁止
184
+ '@typescript-eslint/restrict-plus-operands': 'error',
185
+ // 数値型の配列の sort() を禁止
186
+ '@typescript-eslint/require-array-sort-compare': [
187
+ 'error',
188
+ {
189
+ ignoreStringArrays: true,
190
+ },
191
+ ],
192
+ // Deprecated されたコードの使用を禁止
193
+ '@typescript-eslint/no-deprecated': 'error',
194
+ // 関数の返り値としての void 以外を禁止
195
+ '@typescript-eslint/no-invalid-void-type': 'error',
196
+ },
197
+ }, {
198
+ name: 'eslint-plugin-import-x',
199
+ files: ['**/*.{ts,cts,mts,tsx}'],
200
+ extends: [
201
+ // @ts-expect-error -- languageOptions の型定義が不一致
202
+ importX.flatConfigs.typescript,
203
+ ],
204
+ rules: {
205
+ // import に拡張子を推奨
206
+ 'import-x/extensions': [
207
+ 'warn',
208
+ 'always',
209
+ {
210
+ ignorePackages: true,
211
+ },
212
+ ],
213
+ },
214
+ }, {
215
+ name: '@susisu/eslint-plugin-safe-typescript',
216
+ files: ['**/*.{ts,cts,mts,tsx}'],
217
+ plugins: {
218
+ '@susisu/safe-typescript': safeTypeScriptPlugin,
219
+ },
220
+ rules: safeTypeScriptPlugin.configs.recommended.rules,
221
+ }, {
222
+ name: 'eslint-plugin-tsdoc',
223
+ files: ['**/*.{ts,cts,mts,tsx}'],
224
+ plugins: {
225
+ tsdoc: tsdocPlugin,
226
+ },
227
+ rules: {
228
+ // TSDoc
229
+ 'tsdoc/syntax': 'warn',
230
+ },
231
+ });
@@ -0,0 +1,22 @@
1
+ import { defineConfig } from 'eslint/config';
2
+ import ymlPlugin from 'eslint-plugin-yml';
3
+ export const yaml = defineConfig({
4
+ name: 'eslint-plugin-yml',
5
+ files: ['**/*.{yml,yaml}'],
6
+ extends: [
7
+ ymlPlugin.configs['flat/standard'],
8
+ ],
9
+ rules: {
10
+ 'yml/quotes': ['error', { prefer: 'double' }],
11
+ },
12
+ }, {
13
+ name: 'eslint-plugin-yml (GitHub Workflow)',
14
+ files: ['.github/workflows/*.{yml,yaml}'],
15
+ rules: {
16
+ // ダブルクォートが使えない場合があるのでシングルに統一
17
+ // https://github.com/actions/runner/issues/866
18
+ 'yml/quotes': ['error', { prefer: 'single' }],
19
+ // workflow_dispatch: のような空のマップを許可する
20
+ 'yml/no-empty-mapping-value': 'off',
21
+ },
22
+ });
@@ -0,0 +1,9 @@
1
+ import { defineConfig } from 'eslint/config';
2
+ export const cloudflareWorkers = defineConfig({
3
+ files: ['**/src/worker.{js,ts}', '**/functions/**/*.{js,ts}'],
4
+ rules: {
5
+ // default export を許可
6
+ 'import-x/no-default-export': 'off',
7
+ 'import-x/no-anonymous-default-export': 'off',
8
+ },
9
+ });
@@ -0,0 +1,50 @@
1
+ import { defineConfig } from 'eslint/config';
2
+ import nPlugin from 'eslint-plugin-n';
3
+ export const node = defineConfig({
4
+ name: 'eslint-plugin-n',
5
+ files: [
6
+ '**/bin/**/*.{js,cjs,mjs,ts,cts,mts}',
7
+ '**/*.config.{js,cjs,mjs,ts,cts,mts}',
8
+ '**/codegen.{js,cjs,mjs,ts,cts,mts}',
9
+ ],
10
+ extends: [nPlugin.configs['flat/recommended']],
11
+ rules: {
12
+ // default export を許可
13
+ 'import-x/no-default-export': 'off',
14
+ 'import-x/no-anonymous-default-export': 'off',
15
+ // import で devDependencies を許可
16
+ 'n/no-unpublished-import': 'off',
17
+ 'import-x/no-extraneous-dependencies': [
18
+ 'error',
19
+ {
20
+ devDependencies: true,
21
+ },
22
+ ],
23
+ // 非同期メソッドを優先
24
+ 'n/prefer-promises/dns': 'error',
25
+ 'n/prefer-promises/fs': 'error',
26
+ // 構文のバージョンチェックを無効化
27
+ 'n/no-unsupported-features/es-syntax': 'off',
28
+ // 不正確な import チェックを無効化
29
+ 'n/no-missing-import': 'off',
30
+ 'n/no-extraneous-import': 'off',
31
+ },
32
+ }, {
33
+ // CommonJS
34
+ name: 'eslint-plugin-n',
35
+ files: [
36
+ '**/bin/**/*.{cjs,cts}',
37
+ '**/*.config.{cjs,cts}',
38
+ '**/codegen.{cjs,cts}',
39
+ ],
40
+ extends: [nPlugin.configs['flat/recommended-script']],
41
+ }, {
42
+ // ESM
43
+ name: 'eslint-plugin-n',
44
+ files: [
45
+ '**/bin/**/*.{mjs,mts}',
46
+ '**/*.config.{mjs,mts}',
47
+ '**/codegen.{mjs,mts}',
48
+ ],
49
+ extends: [nPlugin.configs['flat/recommended-module']],
50
+ });
@@ -0,0 +1,27 @@
1
+ import { defineConfig } from 'eslint/config';
2
+ import userScriptsPlugin from 'eslint-plugin-userscripts';
3
+ export const userScript = defineConfig({
4
+ name: 'eslint-plugin-userscripts',
5
+ files: ['**/*.user.js'],
6
+ plugins: {
7
+ userScripts: userScriptsPlugin,
8
+ },
9
+ rules: {
10
+ ...userScriptsPlugin.configs.recommended.rules,
11
+ 'no-undef': 'off',
12
+ 'xss/no-mixed-html': 'off',
13
+ 'xss/no-location-href-assign': 'off',
14
+ 'userscripts/compat-grant': [
15
+ 'error',
16
+ {
17
+ requireAllCompatible: true,
18
+ },
19
+ ],
20
+ 'userscripts/compat-headers': [
21
+ 'error',
22
+ {
23
+ requireAllCompatible: true,
24
+ },
25
+ ],
26
+ },
27
+ });
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from 'eslint/config';
2
+ import jestPlugin from 'eslint-plugin-jest';
3
+ export const jest = defineConfig({
4
+ name: 'eslint-plugin-jest',
5
+ files: [
6
+ '**/*.test.{js,cjs,mjs,jsx,ts,cts,mts,tsx}',
7
+ '**/test/**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}',
8
+ ],
9
+ extends: [jestPlugin.configs['flat/all']],
10
+ });
@@ -0,0 +1,20 @@
1
+ import nextPlugin from '@next/eslint-plugin-next';
2
+ import { defineConfig } from 'eslint/config';
3
+ export const nextJs = defineConfig({
4
+ name: '@next/eslint-plugin-next',
5
+ files: [
6
+ // Pages Router
7
+ '**/pages/**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}',
8
+ // App Router
9
+ '**/app/**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}',
10
+ ],
11
+ extends: [
12
+ nextPlugin.configs.recommended,
13
+ nextPlugin.configs['core-web-vitals'],
14
+ ],
15
+ rules: {
16
+ 'import-x/no-default-export': 'off',
17
+ 'react-refresh/only-export-components': 'off',
18
+ },
19
+ ignores: ['**/.next/**'],
20
+ });
@@ -0,0 +1,128 @@
1
+ import { defineConfig } from 'eslint/config';
2
+ // @ts-expect-error 型定義ファイルがない
3
+ import jsxA11y from 'eslint-plugin-jsx-a11y';
4
+ import reactPlugin from 'eslint-plugin-react';
5
+ import reactHooksPlugin from 'eslint-plugin-react-hooks';
6
+ import reactRefreshPlugin from 'eslint-plugin-react-refresh';
7
+ import globals from 'globals';
8
+ export const react = defineConfig([
9
+ {
10
+ name: 'eslint-plugin-react',
11
+ files: ['**/*.{jsx,tsx}'],
12
+ extends: [
13
+ reactPlugin.configs.flat.recommended,
14
+ reactPlugin.configs.flat['jsx-runtime'],
15
+ ],
16
+ languageOptions: {
17
+ parserOptions: {
18
+ ecmaFeatures: {
19
+ jsx: true,
20
+ },
21
+ lib: ['dom'],
22
+ },
23
+ globals: globals.browser,
24
+ },
25
+ settings: {
26
+ react: {
27
+ version: 'detect',
28
+ },
29
+ },
30
+ rules: {
31
+ // <div flag={true} /> 👉 <div flag />
32
+ 'react/jsx-boolean-value': 'error',
33
+ // <div value={'test'} /> 👉 <div value='test' />
34
+ 'react/jsx-curly-brace-presence': 'error',
35
+ // <div></div> 👉 <div />
36
+ 'react/self-closing-comp': [
37
+ 'error',
38
+ {
39
+ component: true,
40
+ html: true,
41
+ },
42
+ ],
43
+ // コンポーネント名を PascalCase に強制
44
+ 'react/jsx-pascal-case': 'error',
45
+ // ハンドラーの名前規則
46
+ 'react/jsx-handler-names': 'error',
47
+ // useState の分解宣言 & setXXX という名前を強制
48
+ 'react/hook-use-state': 'error',
49
+ // <React.Fragment /> 👉 </>
50
+ 'react/jsx-fragments': 'error',
51
+ // ステートレス関数を優先
52
+ 'react/prefer-stateless-function': 'error',
53
+ // props を並び替える
54
+ 'react/jsx-sort-props': [
55
+ 'error',
56
+ {
57
+ callbacksLast: true,
58
+ shorthandFirst: true,
59
+ multiline: 'last',
60
+ reservedFirst: true,
61
+ },
62
+ ],
63
+ // JSX を .tsx でも使えるように
64
+ 'react/jsx-filename-extension': [
65
+ 'error',
66
+ {
67
+ extensions: ['.jsx', '.tsx'],
68
+ },
69
+ ],
70
+ // props に対してスプレッド演算子を使えるように
71
+ 'react/jsx-props-no-spreading': 'off',
72
+ // <></> を使えるように
73
+ 'react/jsx-no-useless-fragment': 'off',
74
+ // defaultProps を使わない
75
+ 'react/require-default-props': 'off',
76
+ // useCallback でコールバックを宣言させる
77
+ 'react/jsx-no-bind': 'warn',
78
+ // コンポーネントの宣言を function Component() {} に強制
79
+ 'react/function-component-definition': [
80
+ 'error',
81
+ {
82
+ namedComponents: 'function-declaration',
83
+ unnamedComponents: 'arrow-function',
84
+ },
85
+ ],
86
+ },
87
+ },
88
+ {
89
+ name: 'eslint-plugin-react',
90
+ files: ['**/*.jsx'],
91
+ rules: {
92
+ 'react/prop-types': 'error',
93
+ },
94
+ },
95
+ ], {
96
+ name: 'eslint-plugin-react-hooks',
97
+ files: ['**/*.{jsx,tsx}'],
98
+ extends: [reactHooksPlugin.configs.flat['recommended-latest']],
99
+ rules: {
100
+ // https://recoiljs.org/docs/introduction/installation/#eslint
101
+ 'react-hooks/rules-of-hooks': 'error',
102
+ 'react-hooks/exhaustive-deps': [
103
+ 'warn',
104
+ {
105
+ additionalHooks: '(useRecoilCallback|useRecoilTransaction_UNSTABLE)',
106
+ },
107
+ ],
108
+ },
109
+ }, {
110
+ name: 'eslint-plugin-jsx-a11y',
111
+ files: ['**/*.{jsx,tsx}'],
112
+ extends: [jsxA11y.flatConfigs.recommended],
113
+ rules: {
114
+ 'jsx-a11y/alt-text': [
115
+ 'warn',
116
+ {
117
+ elements: ['img', 'object', 'area'],
118
+ img: ['Image'],
119
+ object: [],
120
+ area: [],
121
+ },
122
+ ],
123
+ },
124
+ }, {
125
+ name: 'eslint-plugin-react-refresh',
126
+ files: ['**/*.{jsx,tsx}'],
127
+ extends: [reactRefreshPlugin.configs.recommended],
128
+ });
@@ -0,0 +1,15 @@
1
+ import { defineConfig } from 'eslint/config';
2
+ // @ts-expect-error -- 型定義がない
3
+ import relayPlugin from 'eslint-plugin-relay';
4
+ export const relay = defineConfig({
5
+ name: 'eslint-plugin-relay',
6
+ files: ['**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}'],
7
+ extends: [
8
+ relayPlugin.configs['ts-recommended'],
9
+ ],
10
+ plugins: { relay: relayPlugin },
11
+ rules: {
12
+ // 未使用の GraphQL フィールドを禁止
13
+ 'relay/unused-fields': 'error',
14
+ },
15
+ });
@@ -0,0 +1,6 @@
1
+ import { defineConfig } from 'eslint/config';
2
+ import storybookPlugin from 'eslint-plugin-storybook';
3
+ export const storybook = defineConfig(
4
+ // eslint-plugin-storybook
5
+ // @ts-expect-error -- 型定義が不一致
6
+ storybookPlugin.configs['flat/recommended']);
@@ -0,0 +1,7 @@
1
+ import { defineConfig } from 'eslint/config';
2
+ import reactRefreshPlugin from 'eslint-plugin-react-refresh';
3
+ export const vite = defineConfig({
4
+ name: 'eslint-plugin-react-refresh',
5
+ files: ['**/src/**/*.{jsx,tsx}'],
6
+ extends: [reactRefreshPlugin.configs.vite],
7
+ });
@@ -0,0 +1,20 @@
1
+ import vitestPlugin from '@vitest/eslint-plugin';
2
+ import { defineConfig } from 'eslint/config';
3
+ export const vitest = defineConfig({
4
+ name: '@vitest/eslint-plugin',
5
+ files: [
6
+ '**/*.test.{js,cjs,mjs,jsx,ts,cts,mts,tsx}',
7
+ '**/test/**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}',
8
+ ],
9
+ extends: [vitestPlugin.configs.recommended],
10
+ settings: {
11
+ vitest: {
12
+ typecheck: true,
13
+ },
14
+ },
15
+ languageOptions: {
16
+ globals: {
17
+ ...vitestPlugin.environments.env.globals,
18
+ },
19
+ },
20
+ });
package/src/index.js ADDED
@@ -0,0 +1,62 @@
1
+ import { defineConfig } from 'eslint/config';
2
+ import { common } from './base/common.js';
3
+ import { graphql } from './base/graphql.js';
4
+ import { javaScript } from './base/javascript.js';
5
+ import { packageJson } from './base/package.json.js';
6
+ import { typeScript } from './base/typescript.js';
7
+ import { yaml } from './base/yaml.js';
8
+ import { cloudflareWorkers } from './environments/cloudflareWorkers.js';
9
+ import { node } from './environments/node.js';
10
+ import { userScript } from './environments/userscript.js';
11
+ import { jest } from './frameworks/jest.js';
12
+ import { nextJs } from './frameworks/next.js.js';
13
+ import { react } from './frameworks/react.js';
14
+ import { relay } from './frameworks/relay.js';
15
+ import { storybook } from './frameworks/storybook.js';
16
+ import { vite } from './frameworks/vite.js';
17
+ import { vitest } from './frameworks/vitest.js';
18
+ export function config(options, ...overrides) {
19
+ return defineConfig(
20
+ // ベース
21
+ [
22
+ common,
23
+ // JavaScript
24
+ javaScript,
25
+ // TypeScript
26
+ typeScript,
27
+ // GraphQL
28
+ graphql,
29
+ // YAML
30
+ yaml,
31
+ // package.json
32
+ packageJson,
33
+ ],
34
+ // フレームワーク
35
+ [
36
+ // React
37
+ react,
38
+ // Next.js
39
+ nextJs,
40
+ // Vite
41
+ vite,
42
+ // Relay
43
+ relay,
44
+ // Storybook
45
+ storybook,
46
+ // Jest
47
+ jest,
48
+ // Vitest
49
+ vitest,
50
+ ],
51
+ // 環境
52
+ [
53
+ // Node.js
54
+ node,
55
+ // Cloudflare Worker
56
+ cloudflareWorkers,
57
+ // UserScript
58
+ userScript,
59
+ ],
60
+ // @ts-expect-error -- 型定義が不完全
61
+ ...overrides);
62
+ }