@slashnephy/eslint-config 2.0.0 → 2.0.5

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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2023 SlashNephy
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2023 SlashNephy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # eslint-config
2
2
 
3
3
  [![Check](https://github.com/SlashNephy/eslint-config/actions/workflows/check.yml/badge.svg)](https://github.com/SlashNephy/eslint-config/actions/workflows/check.yml?query=branch%3Amaster)
4
- ![npm](https://img.shields.io/npm/v/%40slashnephy%2Feslint-config)
4
+ [![npm](https://img.shields.io/npm/v/%40slashnephy%2Feslint-config)](https://www.npmjs.com/package/@slashnephy/eslint-config)
5
5
 
6
6
  ## Install
7
7
 
@@ -12,6 +12,7 @@ $ yarn add @slashnephy/eslint-config
12
12
  ## Usage
13
13
 
14
14
  `.eslintrc.json`
15
+
15
16
  ```json
16
17
  {
17
18
  "extends": ["@slashnephy/eslint-config"]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slashnephy/eslint-config",
3
- "version": "2.0.0",
3
+ "version": "2.0.5",
4
4
  "type": "commonjs",
5
5
  "main": "./src/index.js",
6
6
  "author": "SlashNephy <spica@starry.blue> (https://spica.starry.blue/)",
@@ -12,6 +12,7 @@
12
12
  "files": [
13
13
  "./package.json",
14
14
  "./src/**/*.js",
15
+ "./src/**/*.ts",
15
16
  "./README.md"
16
17
  ],
17
18
  "publishConfig": {
@@ -27,7 +28,7 @@
27
28
  "lint": "concurrently -n lint: 'yarn:lint:*'",
28
29
  "lint:eslint": "yarn build && eslint .",
29
30
  "lint:prettier": "prettier --check .",
30
- "publish": "yarn clean && yarn build && yarn npm publish"
31
+ "publish": "yarn build && yarn npm publish"
31
32
  },
32
33
  "dependencies": {
33
34
  "@emotion/eslint-plugin": "11.11.0",
@@ -75,7 +76,7 @@
75
76
  "eslint": "^8"
76
77
  },
77
78
  "devDependencies": {
78
- "@slashnephy/prettier-config": "0.1.27",
79
+ "@slashnephy/prettier-config": "1.0.3",
79
80
  "@types/eslint": "8.44.1",
80
81
  "@types/node": "20.4.5",
81
82
  "concurrently": "8.2.0",
@@ -11,7 +11,7 @@ module.exports = {
11
11
  'plugin:import/recommended',
12
12
  'plugin:xss/recommended',
13
13
  ],
14
- plugins: ['import', 'promise', 'n', 'unused-imports', 'deprecation'],
14
+ plugins: ['import', 'promise', 'n', 'unused-imports'],
15
15
  env: {
16
16
  node: true,
17
17
  es2022: true,
@@ -116,7 +116,6 @@ module.exports = {
116
116
  exceptRange: true,
117
117
  },
118
118
  ],
119
- 'deprecation/deprecation': 'error',
120
119
  'unused-imports/no-unused-vars': [
121
120
  'warn',
122
121
  {
@@ -0,0 +1,189 @@
1
+ import standard from 'eslint-config-standard'
2
+
3
+ import type { Linter } from 'eslint'
4
+
5
+ /**
6
+ * JavaScript 関連の eslint プリセット
7
+ */
8
+ module.exports = {
9
+ extends: [
10
+ 'plugin:eslint-comments/recommended',
11
+ 'plugin:node/recommended',
12
+ 'plugin:import/recommended',
13
+ 'plugin:xss/recommended',
14
+ ],
15
+ plugins: ['import', 'promise', 'n', 'unused-imports'],
16
+ env: {
17
+ node: true,
18
+ es2022: true,
19
+ },
20
+ parserOptions: {
21
+ ecmaVersion: 'latest',
22
+ },
23
+ globals: {
24
+ document: 'readonly',
25
+ navigator: 'readonly',
26
+ window: 'readonly',
27
+ },
28
+ rules: {
29
+ ...standard.rules,
30
+ // 不要なルール無効化コメントを報告
31
+ 'eslint-comments/no-unused-disable': 'error',
32
+ // default export を禁止
33
+ 'import/no-default-export': 'error',
34
+ // アロー関数を優先
35
+ 'prefer-arrow-callback': 'error',
36
+ // const a = function () { ... } を禁止
37
+ 'func-style': ['error', 'declaration', { allowArrowFunctions: true }],
38
+ // 中括弧の省略を禁止
39
+ curly: 'error',
40
+ // テンプレート文字列を優先
41
+ 'prefer-template': 'error',
42
+ // == 比較 👉 === 比較
43
+ eqeqeq: 'error',
44
+ // *.js で 'use strict'; を強制
45
+ strict: ['error', 'global'],
46
+ // import 順を並び替える
47
+ 'import/order': [
48
+ 'warn',
49
+ {
50
+ // 組み込み → 外部依存 → 内部依存 → object → type の順にする
51
+ groups: [
52
+ 'builtin',
53
+ 'external',
54
+ ['parent', 'sibling', 'index'],
55
+ 'object',
56
+ 'type',
57
+ 'unknown',
58
+ ],
59
+ // カテゴリー間に改行を入れる
60
+ 'newlines-between': 'always',
61
+ // 大文字小文字区別なしで ABC 順にする
62
+ alphabetize: {
63
+ order: 'asc',
64
+ caseInsensitive: true,
65
+ },
66
+ pathGroups: [
67
+ // **.css は最後に配置する
68
+ {
69
+ pattern: '**.css',
70
+ group: 'type',
71
+ position: 'after',
72
+ },
73
+ ],
74
+ // **.css が import 順最後ではないときに警告
75
+ warnOnUnassignedImports: true,
76
+ },
77
+ ],
78
+ // 不要 import 文を禁止
79
+ 'unused-imports/no-unused-imports': 'error',
80
+ // 特定の構文を禁止
81
+ 'no-restricted-syntax': [
82
+ 'error',
83
+ // 数値リテラル以外での Array#at() の使用を禁止
84
+ // https://qiita.com/printf_moriken/items/da03f55cb626617c1958
85
+ {
86
+ selector:
87
+ // eslint-disable-next-line quotes
88
+ "CallExpression[callee.property.name='at']:not([arguments.0.type='Literal'],[arguments.0.type='UnaryExpression'][arguments.0.argument.type='Literal'])",
89
+ message: 'at method accepts only a literal argument',
90
+ },
91
+ ],
92
+ // 非同期メソッドを優先
93
+ 'node/prefer-promises/dns': 'error',
94
+ 'node/prefer-promises/fs': 'error',
95
+ // 構文のバージョンチェックを無効化
96
+ 'node/no-unsupported-features/es-syntax': 'off',
97
+ // 不正確な import チェックを無効化
98
+ 'node/no-missing-import': 'off',
99
+ 'node/no-extraneous-import': 'off',
100
+ 'node/no-unpublished-import': 'off',
101
+ 'import/no-import-module-exports': 'off',
102
+ 'import/no-extraneous-dependencies': 'off',
103
+ // foo["bar"] 👉 foo.bar
104
+ 'dot-notation': 'error',
105
+ // {foo: foo} 👉 {foo}
106
+ 'object-shorthand': ['error', 'always'],
107
+ // Array 系メソッドで return を強制
108
+ 'array-callback-return': ['error'],
109
+ // ループ内では await を禁止
110
+ 'no-await-in-loop': 'error',
111
+ // 操作が値に影響しない式を禁止
112
+ 'no-constant-binary-expression': 'error',
113
+ // コンストラクター内で return を禁止
114
+ 'no-constructor-return': 'error',
115
+ // 関数の返り値としての Promise executor を禁止
116
+ 'no-promise-executor-return': 'error',
117
+ // 自身との比較 (e.g. foo === foo) を禁止
118
+ 'no-self-compare': 'error',
119
+ // 非テンプレート文字列で ${foo} を禁止
120
+ // "Hello, ${name}" 👉 `Hello, ${name}`
121
+ 'no-template-curly-in-string': 'error',
122
+ // 更新されないループ条件を禁止
123
+ 'no-unmodified-loop-condition': 'error',
124
+ // 到達できないループを禁止
125
+ 'no-unreachable-loop': 'error',
126
+ // 未使用の private メンバーを禁止
127
+ 'no-unused-private-class-members': 'error',
128
+ // スレッドセーフで安全に更新されないコードを禁止
129
+ 'require-atomic-updates': 'error',
130
+ // func () 👉 func()
131
+ 'func-call-spacing': ['error', 'never'],
132
+ // ペアになっていない setter を禁止
133
+ 'accessor-pairs': 'error',
134
+ // キャメルケースに強制しない
135
+ camelcase: 'off',
136
+ // switch 文で default を強制しない
137
+ 'default-case': 'off',
138
+ // default export を優先しない
139
+ 'import/prefer-default-export': 'off',
140
+ // 循環 import を禁止
141
+ 'import/no-cycle': 'error',
142
+ // continue 文を許可
143
+ 'no-continue': 'off',
144
+ // _ で始まるメンバー名を許可
145
+ 'no-underscore-dangle': 'off',
146
+ // 自身より後に宣言されたメンバーの使用を許可
147
+ 'no-use-before-define': 'off',
148
+ // console.* の使用を許可
149
+ 'no-console': 'off',
150
+ // 深い三項演算子を許可
151
+ 'no-nested-ternary': 'off',
152
+ // i++ インクリメントを許可
153
+ 'no-plusplus': 'off',
154
+ // return の省略などを許可
155
+ 'consistent-return': 'off',
156
+ // 空行を挟む
157
+ 'padding-line-between-statements': [
158
+ 'warn',
159
+ // return 前に空行
160
+ { blankLine: 'always', prev: '*', next: 'return' },
161
+ // ディレクティブ後に空行
162
+ { blankLine: 'always', prev: 'directive', next: '*' },
163
+ { blankLine: 'any', prev: 'directive', next: 'directive' },
164
+ ],
165
+ // void Promise を許可
166
+ 'no-void': 'off',
167
+ // 1 <= x < 10 を許可
168
+ yoda: [
169
+ 'error',
170
+ 'never',
171
+ {
172
+ exceptRange: true,
173
+ },
174
+ ],
175
+ // 不要な変数を禁止
176
+ 'unused-imports/no-unused-vars': [
177
+ 'warn',
178
+ // '_' で始まる変数を許可
179
+ {
180
+ vars: 'all',
181
+ varsIgnorePattern: '^_',
182
+ args: 'after-used',
183
+ argsIgnorePattern: '^_',
184
+ },
185
+ ],
186
+ // anonymous な export default を許可
187
+ 'import/no-anonymous-default-export': 'off',
188
+ },
189
+ } satisfies Linter.Config
@@ -0,0 +1,5 @@
1
+ import type { Linter } from 'eslint'
2
+
3
+ module.exports = {
4
+ extends: ['plugin:json/recommended'],
5
+ } satisfies Linter.Config
@@ -8,7 +8,7 @@ module.exports = {
8
8
  'plugin:no-void-return-type/recommended',
9
9
  'plugin:storybook/recommended',
10
10
  ],
11
- plugins: ['@typescript-eslint', 'tsdoc'],
11
+ plugins: ['@typescript-eslint', 'tsdoc', 'deprecation'],
12
12
  parser: '@typescript-eslint/parser',
13
13
  parserOptions: {
14
14
  sourceType: 'module',
@@ -162,5 +162,6 @@ module.exports = {
162
162
  allowBitwiseExpressions: true,
163
163
  },
164
164
  ],
165
+ 'deprecation/deprecation': 'error',
165
166
  },
166
167
  };
@@ -0,0 +1,200 @@
1
+ import type { Linter } from 'eslint'
2
+
3
+ /**
4
+ * TypeScript 関連の eslint プリセット
5
+ */
6
+ module.exports = {
7
+ extends: [
8
+ 'plugin:@typescript-eslint/strict-type-checked',
9
+ 'plugin:@typescript-eslint/stylistic-type-checked',
10
+ 'plugin:import/typescript',
11
+ 'plugin:no-void-return-type/recommended',
12
+ 'plugin:storybook/recommended',
13
+ ],
14
+ plugins: ['@typescript-eslint', 'tsdoc', 'deprecation'],
15
+ parser: '@typescript-eslint/parser',
16
+ parserOptions: {
17
+ sourceType: 'module',
18
+ ecmaVersion: 'latest',
19
+ lib: ['esnext'],
20
+ project: './tsconfig.json',
21
+ warnOnUnsupportedTypeScriptVersion: true,
22
+ },
23
+ settings: {
24
+ 'import/parsers': {
25
+ '@typescript-eslint/parser': ['.ts', '.mts', '.cts', '.tsx', '.d.ts'],
26
+ },
27
+ 'import/resolver': {
28
+ node: {
29
+ extensions: ['.js', '.jsx', '.ts', '.tsx'],
30
+ },
31
+ typescript: {
32
+ alwaysTryTypes: true,
33
+ },
34
+ },
35
+ },
36
+ rules: {
37
+ /**
38
+ * Automatically fixable は error にする
39
+ */
40
+ // interface 👉 type
41
+ '@typescript-eslint/consistent-type-definitions': ['error', 'type'],
42
+ // export type を優先
43
+ '@typescript-eslint/consistent-type-exports': [
44
+ 'error',
45
+ {
46
+ fixMixedExportsWithInlineTypeSpecifier: true,
47
+ },
48
+ ],
49
+ // import type を優先
50
+ '@typescript-eslint/consistent-type-imports': 'error',
51
+ // クラスのアクセス修飾子を強制
52
+ '@typescript-eslint/explicit-member-accessibility': 'error',
53
+ // export されているメンバーや public メンバーは型を明示させる
54
+ '@typescript-eslint/explicit-module-boundary-types': 'warn',
55
+ '@typescript-eslint/member-delimiter-style': 'error',
56
+ '@typescript-eslint/member-ordering': 'warn',
57
+ '@typescript-eslint/method-signature-style': ['warn', 'method'],
58
+ // 命名規則を強制
59
+ '@typescript-eslint/naming-convention': [
60
+ 'warn',
61
+ // デフォルトは camelCase
62
+ {
63
+ selector: ['default'],
64
+ format: ['strictCamelCase'],
65
+ },
66
+ // 型名 / 列挙型のメンバーは PascalCase
67
+ {
68
+ selector: ['typeLike', 'enumMember'],
69
+ format: ['StrictPascalCase'],
70
+ },
71
+ // 変数名は camelCase
72
+ {
73
+ selector: ['variableLike'],
74
+ format: ['strictCamelCase', 'StrictPascalCase'],
75
+ leadingUnderscore: 'allow',
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/require-array-sort-compare': 'off',
166
+ '@typescript-eslint/switch-exhaustiveness-check': 'error',
167
+ '@typescript-eslint/type-annotation-spacing': 'error',
168
+ '@typescript-eslint/unbound-method': 'off',
169
+ // JavaScript 側で定義
170
+ '@typescript-eslint/no-unused-vars': 'off',
171
+ // 過激なルールを無効化
172
+ '@typescript-eslint/no-unsafe-assignment': 'off',
173
+ '@typescript-eslint/no-unsafe-call': 'off',
174
+ '@typescript-eslint/no-unsafe-member-access': 'off',
175
+ '@typescript-eslint/no-unsafe-return': 'off',
176
+ '@typescript-eslint/no-unsafe-argument': 'off',
177
+ '@typescript-eslint/no-unsafe-enum-comparison': 'off',
178
+ '@typescript-eslint/restrict-template-expressions': 'off',
179
+ '@typescript-eslint/strict-boolean-expressions': 'off',
180
+ // import に拡張子を推奨
181
+ 'import/extensions': [
182
+ 'warn',
183
+ 'always',
184
+ {
185
+ ignorePackages: true,
186
+ },
187
+ ],
188
+ // TSDoc
189
+ 'tsdoc/syntax': 'warn',
190
+ // enum のメンバーでビット演算を許可する
191
+ '@typescript-eslint/prefer-literal-enum-member': [
192
+ 'error',
193
+ {
194
+ allowBitwiseExpressions: true,
195
+ },
196
+ ],
197
+ // Deprecated されたコードの使用を禁止
198
+ 'deprecation/deprecation': 'error',
199
+ },
200
+ } satisfies Linter.Config
@@ -0,0 +1,19 @@
1
+ import type { Linter } from 'eslint'
2
+
3
+ module.exports = {
4
+ extends: ['plugin:yml/standard', 'plugin:yml/prettier'],
5
+ parser: 'yaml-eslint-parser',
6
+ rules: {
7
+ 'yml/quotes': ['error', { prefer: 'double' }],
8
+ },
9
+ overrides: [
10
+ // GitHub Workflow でダブルクォートが使えない場合があるのでシングルに統一
11
+ // https://github.com/actions/runner/issues/866
12
+ {
13
+ files: '.github/workflows/*.{yml,yaml}',
14
+ rules: {
15
+ 'yml/quotes': ['error', { prefer: 'single' }],
16
+ },
17
+ },
18
+ ],
19
+ } satisfies Linter.Config
@@ -0,0 +1,20 @@
1
+ import type { Linter } from 'eslint'
2
+
3
+ /**
4
+ * Emotion 関連の eslint プリセット
5
+ */
6
+ module.exports = {
7
+ plugins: ['@emotion', 'css-reorder'],
8
+ rules: {
9
+ '@emotion/pkg-renaming': 'error',
10
+ '@emotion/styled-import': 'error',
11
+ '@emotion/syntax-preference': 'error',
12
+ 'css-reorder/property-reorder': 'error',
13
+ 'react/no-unknown-property': [
14
+ 'error',
15
+ {
16
+ ignore: ['css'],
17
+ },
18
+ ],
19
+ },
20
+ } satisfies Linter.Config
@@ -0,0 +1,17 @@
1
+ import type { Linter } from 'eslint'
2
+
3
+ /**
4
+ * jest 関連の eslint プリセット
5
+ */
6
+ module.exports = {
7
+ plugins: ['jest'],
8
+ extends: [
9
+ 'plugin:jest/recommended',
10
+ 'plugin:jest/style',
11
+ 'plugin:jest-formatting/recommended',
12
+ ],
13
+ env: {
14
+ 'jest/globals': true,
15
+ },
16
+ rules: {},
17
+ } satisfies Linter.Config
@@ -0,0 +1,12 @@
1
+ import next from 'eslint-config-next'
2
+
3
+ import type { Linter } from 'eslint'
4
+
5
+ // https://github.com/vercel/next.js/blob/canary/packages/eslint-config-next/index.js
6
+ module.exports = {
7
+ extends: ['plugin:@next/next/recommended'],
8
+ rules: {
9
+ ...next.rules,
10
+ 'import/no-default-export': 'off',
11
+ },
12
+ } satisfies Linter.Config
@@ -0,0 +1,92 @@
1
+ import type { Linter } from 'eslint'
2
+
3
+ /**
4
+ * React 関連の eslint プリセット
5
+ */
6
+ module.exports = {
7
+ extends: [
8
+ 'plugin:react/recommended',
9
+ 'plugin:react/jsx-runtime',
10
+ 'plugin:css-import-order/recommended',
11
+ ],
12
+ plugins: ['react-hooks', 'css-import-order'],
13
+ parserOptions: {
14
+ ecmaFeatures: {
15
+ jsx: true,
16
+ },
17
+ jsxPragma: 'React',
18
+ lib: ['dom'],
19
+ },
20
+ env: {
21
+ browser: true,
22
+ node: true,
23
+ },
24
+ settings: {
25
+ react: {
26
+ version: 'detect',
27
+ },
28
+ },
29
+ rules: {
30
+ // <div flag={true} /> 👉 <div flag />
31
+ 'react/jsx-boolean-value': 'error',
32
+ // <div value={'test'} /> 👉 <div value='test' />
33
+ 'react/jsx-curly-brace-presence': 'error',
34
+ // <div></div> 👉 <div />
35
+ 'react/self-closing-comp': [
36
+ 'error',
37
+ {
38
+ component: true,
39
+ html: true,
40
+ },
41
+ ],
42
+ // コンポーネント名を PascalCase に強制
43
+ 'react/jsx-pascal-case': 'error',
44
+ // https://recoiljs.org/docs/introduction/installation/#eslint
45
+ 'react-hooks/rules-of-hooks': 'error',
46
+ 'react-hooks/exhaustive-deps': [
47
+ 'warn',
48
+ {
49
+ additionalHooks: '(useRecoilCallback|useRecoilTransaction_UNSTABLE)',
50
+ },
51
+ ],
52
+ // ハンドラーの名前規則
53
+ 'react/jsx-handler-names': 'error',
54
+ // useState の分解宣言 & setXXX という名前を強制
55
+ 'react/hook-use-state': 'error',
56
+ // <React.Fragment /> 👉 </>
57
+ 'react/jsx-fragments': 'error',
58
+ // ステートレス関数を優先
59
+ 'react/prefer-stateless-function': 'error',
60
+ // props を並び替える
61
+ 'react/jsx-sort-props': [
62
+ 'error',
63
+ {
64
+ callbacksLast: true,
65
+ shorthandFirst: true,
66
+ multiline: 'last',
67
+ reservedFirst: true,
68
+ },
69
+ ],
70
+ // JSX を .tsx でも使えるように
71
+ 'react/jsx-filename-extension': [
72
+ 'error',
73
+ {
74
+ extensions: ['.jsx', '.tsx'],
75
+ },
76
+ ],
77
+ // props に対してスプレッド演算子を使えるように
78
+ 'react/jsx-props-no-spreading': 'off',
79
+ // <></> を使えるように
80
+ 'react/jsx-no-useless-fragment': 'off',
81
+ // defaultProps を使わない
82
+ 'react/require-default-props': 'off',
83
+ },
84
+ overrides: [
85
+ {
86
+ files: '**/*.jsx',
87
+ rules: {
88
+ 'react/prop-types': 'error',
89
+ },
90
+ },
91
+ ],
92
+ } satisfies Linter.Config
@@ -0,0 +1,10 @@
1
+ import type { Linter } from 'eslint'
2
+
3
+ module.exports = {
4
+ extends: ['plugin:relay/recommended'],
5
+ plugins: ['relay'],
6
+ rules: {
7
+ // 未使用の GraphQL フィールドを禁止
8
+ 'relay/unused-fields': 'error',
9
+ },
10
+ } satisfies Linter.Config
@@ -0,0 +1,8 @@
1
+ import type { Linter } from 'eslint'
2
+
3
+ module.exports = {
4
+ plugins: ['react-refresh'],
5
+ rules: {
6
+ 'react-refresh/only-export-components': 'warn',
7
+ },
8
+ } satisfies Linter.Config
@@ -0,0 +1,9 @@
1
+ import type { Linter } from 'eslint'
2
+
3
+ /**
4
+ * vitest 関連の eslint プリセット
5
+ */
6
+ module.exports = {
7
+ plugins: ['vitest'],
8
+ rules: {},
9
+ } satisfies Linter.Config
package/src/index.ts ADDED
@@ -0,0 +1,134 @@
1
+ import { resolve } from 'path'
2
+
3
+ // https://github.com/microsoft/rushstack/tree/main/eslint/eslint-patch
4
+ import '@rushstack/eslint-patch/modern-module-resolution.js'
5
+ import { readGitignoreFiles } from 'eslint-gitignore'
6
+
7
+ import type { Linter } from 'eslint'
8
+
9
+ module.exports = {
10
+ root: true,
11
+ extends: ['eslint:recommended'],
12
+ ignorePatterns: [
13
+ '**/node_modules/**',
14
+ '**/.yarn/**',
15
+ '**/dist/**',
16
+ ...readGitignoreFiles(),
17
+ ],
18
+ overrides: [
19
+ /*
20
+ * 言語固有ルール
21
+ */
22
+ // JavaScript / TypeScript
23
+ {
24
+ files: '**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx}',
25
+ extends: resolve(__dirname, 'base/javascript.js'),
26
+ },
27
+ // TypeScript
28
+ {
29
+ files: '**/*.{ts,mts,cts,tsx}',
30
+ extends: [resolve(__dirname, 'base/typescript.js')],
31
+ },
32
+ // JSON
33
+ {
34
+ files: '**/*.json',
35
+ extends: resolve(__dirname, 'base/json.js'),
36
+ },
37
+ // YAML
38
+ {
39
+ files: '**/*.{yml,yaml}',
40
+ extends: resolve(__dirname, 'base/yaml.js'),
41
+ },
42
+
43
+ /*
44
+ * フレームワーク固有ルール
45
+ */
46
+ // React
47
+ {
48
+ files: '**/*.{jsx,tsx}',
49
+ extends: resolve(__dirname, 'framework/react.js'),
50
+ },
51
+ // Relay
52
+ {
53
+ files: '**/*.{js,jsx,ts,tsx}',
54
+ extends: resolve(__dirname, 'framework/relay.js'),
55
+ },
56
+ // Next.js
57
+ {
58
+ files: [
59
+ // <= 12
60
+ '**/pages/**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx}',
61
+ // >= 13
62
+ '**/src/app/**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx}',
63
+ ],
64
+ extends: resolve(__dirname, 'framework/next.js.js'),
65
+ },
66
+ // Vite
67
+ {
68
+ files: '**/src/**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx}',
69
+ extends: resolve(__dirname, 'framework/vite.js'),
70
+ },
71
+ // Emotion
72
+ {
73
+ files: '**/*.{jsx,tsx}',
74
+ extends: resolve(__dirname, 'framework/emotion.js'),
75
+ },
76
+ // jest / vitest
77
+ {
78
+ files: [
79
+ '**/*.test.{js,mjs,cjs,jsx,ts,mts,cts,tsx}',
80
+ '**/test/**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx}',
81
+ ],
82
+ extends: [
83
+ resolve(__dirname, 'framework/jest.js'),
84
+ resolve(__dirname, 'framework/vitest.js'),
85
+ ],
86
+ },
87
+
88
+ /*
89
+ * 個別のプリセットルール
90
+ */
91
+ // React向け a11y
92
+ {
93
+ files: '**/*.{jsx,tsx}',
94
+ extends: resolve(__dirname, 'presets/a11y.js'),
95
+ },
96
+ // Node.js
97
+ {
98
+ files: '**/bin/**/*.{js,mjs,cjs,ts,mts,cts}',
99
+ extends: resolve(__dirname, 'presets/node.js'),
100
+ },
101
+ // Cloudflare Worker
102
+ {
103
+ files: ['**/src/worker.{js,ts}', '**/functions/**/*.{js,ts}'],
104
+ extends: resolve(__dirname, 'presets/allow-default-export.js'),
105
+ },
106
+ // ビルドツールの構成ファイル
107
+ {
108
+ files: [
109
+ '**/{webpack,rollup,vite}.config.{js,mjs,cjs,ts,mts,cts}',
110
+ '**/codegen.{js,mjs,cjs,ts,mts,cts}',
111
+ ],
112
+ extends: [
113
+ resolve(__dirname, 'presets/allow-default-export.js'),
114
+ resolve(__dirname, 'presets/build-configuration.js'),
115
+ resolve(__dirname, 'presets/node.js'),
116
+ ],
117
+ },
118
+ // package.json
119
+ {
120
+ files: '**/package.json',
121
+ extends: resolve(__dirname, 'presets/package.json.js'),
122
+ },
123
+ // UserScript
124
+ {
125
+ files: '**/*.user.js',
126
+ extends: resolve(__dirname, 'presets/userscript.js'),
127
+ },
128
+ // コーディングスタイル
129
+ {
130
+ files: '**/*',
131
+ extends: resolve(__dirname, 'presets/style.js'),
132
+ },
133
+ ],
134
+ } satisfies Linter.Config
@@ -0,0 +1,27 @@
1
+ // @ts-expect-error Linter.Config
2
+ import airbnb from 'eslint-config-airbnb/rules/react-a11y'
3
+
4
+ import type { Linter } from 'eslint'
5
+
6
+ module.exports = {
7
+ extends: ['plugin:jsx-a11y/recommended'],
8
+ plugins: ['jsx-a11y'],
9
+ parserOptions: {
10
+ ecmaFeatures: {
11
+ jsx: true,
12
+ },
13
+ },
14
+ rules: {
15
+ ...airbnb.rules,
16
+ 'jsx-a11y/alt-text': [
17
+ 'warn',
18
+ {
19
+ elements: ['img', 'object', 'area', 'input[type="image"]'],
20
+ img: ['Image'],
21
+ object: [],
22
+ area: [],
23
+ 'input[type="image"]': [],
24
+ },
25
+ ],
26
+ },
27
+ } satisfies Linter.Config
@@ -0,0 +1,7 @@
1
+ import type { Linter } from 'eslint'
2
+
3
+ module.exports = {
4
+ rules: {
5
+ 'import/no-default-export': 'off',
6
+ },
7
+ } satisfies Linter.Config
@@ -0,0 +1,13 @@
1
+ import type { Linter } from 'eslint'
2
+
3
+ module.exports = {
4
+ rules: {
5
+ // import で devDependencies を許可
6
+ 'import/no-extraneous-dependencies': [
7
+ 'error',
8
+ {
9
+ devDependencies: true,
10
+ },
11
+ ],
12
+ },
13
+ } satisfies Linter.Config
@@ -0,0 +1,8 @@
1
+ import type { Linter } from 'eslint'
2
+
3
+ module.exports = {
4
+ parserOptions: {
5
+ // Node.js 用の tsconfig.json を参照
6
+ project: './tsconfig.node.json',
7
+ },
8
+ } satisfies Linter.Config
@@ -0,0 +1,37 @@
1
+ import type { Linter } from 'eslint'
2
+
3
+ module.exports = {
4
+ extends: ['plugin:package-json/recommended'],
5
+ plugins: ['package-json'],
6
+ env: {
7
+ node: true,
8
+ },
9
+ rules: {
10
+ 'package-json/order-properties': [
11
+ 'error',
12
+ [
13
+ 'name',
14
+ 'version',
15
+ 'type',
16
+ 'main',
17
+ 'private',
18
+ 'author',
19
+ 'description',
20
+ 'repository',
21
+ 'license',
22
+ 'files',
23
+ 'exports',
24
+ 'publishConfig',
25
+ 'scripts',
26
+ 'dependencies',
27
+ 'peerDependencies',
28
+ 'optionalDependencies',
29
+ 'bundledDependencies',
30
+ 'devDependencies',
31
+ 'packageManager',
32
+ 'engines',
33
+ 'eslintConfig',
34
+ ],
35
+ ],
36
+ },
37
+ } satisfies Linter.Config
@@ -0,0 +1,39 @@
1
+ import type { Linter } from 'eslint'
2
+
3
+ /**
4
+ * スタイル関連の eslint プリセット
5
+ * - 特に eslint と競合しそうなルールを記述する
6
+ */
7
+ module.exports = {
8
+ extends: ['plugin:editorconfig/noconflict', 'prettier'],
9
+ plugins: ['editorconfig'],
10
+ rules: {
11
+ // インデント
12
+ // - 基本インデントを 2 にする
13
+ // - switch-case でインデントを許可
14
+ // indent: ['error', 2, { SwitchCase: 1 }],
15
+ // 詳細なインデントは競合するので無効にして prettier に任せる
16
+
17
+ // ダブルクォーテーションを禁止
18
+ quotes: ['error', 'single'],
19
+ // セミコロンを禁止
20
+ semi: ['error', 'never'],
21
+ // UTF-8 BOM を禁止
22
+ 'unicode-bom': ['error', 'never'],
23
+ // 最終行に改行を挿入
24
+ 'eol-last': ['error', 'always'],
25
+ // 行末のスペースを禁止
26
+ 'no-trailing-spaces': ['error'],
27
+ },
28
+ overrides: [
29
+ // JSON ではダブルクォーテーションを使う
30
+ {
31
+ files: '**/*.json',
32
+ rules: {
33
+ quotes: ['error', 'double'],
34
+ semi: 'off',
35
+ 'eol-last': 'off',
36
+ },
37
+ },
38
+ ],
39
+ } satisfies Linter.Config
@@ -0,0 +1,25 @@
1
+ import type { Linter } from 'eslint'
2
+
3
+ /**
4
+ * UserScript 開発用の eslint プリセット
5
+ */
6
+ module.exports = {
7
+ extends: ['plugin:userscripts/recommended'],
8
+ rules: {
9
+ 'no-undef': 'off',
10
+ 'xss/no-mixed-html': 'off',
11
+ 'xss/no-location-href-assign': 'off',
12
+ 'userscripts/compat-grant': [
13
+ 'error',
14
+ {
15
+ requireAllCompatible: true,
16
+ },
17
+ ],
18
+ 'userscripts/compat-headers': [
19
+ 'error',
20
+ {
21
+ requireAllCompatible: true,
22
+ },
23
+ ],
24
+ },
25
+ } satisfies Linter.Config