eslint-plugin-smarthr 3.4.0 → 3.5.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/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ## [3.5.1](https://github.com/kufu/tamatebako/compare/eslint-plugin-smarthr-v3.5.0...eslint-plugin-smarthr-v3.5.1) (2025-12-31)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * best-practice-for-rest-parameters で残余引数をarrow functionでreturnする場合誤検知されていた問題を修正 ([#988](https://github.com/kufu/tamatebako/issues/988)) ([4353323](https://github.com/kufu/tamatebako/commit/4353323e1cc37761dbc841023f5b1575ca3e38cc))
11
+ * best-practice-for-rest-parameters で残余引数をreturnする場合誤検知されていた問題を修正 ([#986](https://github.com/kufu/tamatebako/issues/986)) ([9ff646c](https://github.com/kufu/tamatebako/commit/9ff646c10c6ca9c28eff111b837fa6030003ac2a))
12
+
13
+ ## [3.5.0](https://github.com/kufu/tamatebako/compare/eslint-plugin-smarthr-v3.4.0...eslint-plugin-smarthr-v3.5.0) (2025-12-26)
14
+
15
+
16
+ ### Features
17
+
18
+ * smarthr/best-practice-for-optional-chaining を追加 ([#979](https://github.com/kufu/tamatebako/issues/979)) ([ff18a55](https://github.com/kufu/tamatebako/commit/ff18a55970f026c2d828cf22f99e9969d90d7d82))
19
+ * smarthr/best-practice-for-unnesessary-early-return を追加 ([#977](https://github.com/kufu/tamatebako/issues/977)) ([cd0b1a2](https://github.com/kufu/tamatebako/commit/cd0b1a205e82f01ce2be4fe43b69555d86ab5fd1))
20
+
5
21
  ## [3.4.0](https://github.com/kufu/tamatebako/compare/eslint-plugin-smarthr-v3.3.2...eslint-plugin-smarthr-v3.4.0) (2025-12-24)
6
22
 
7
23
 
package/README.md CHANGED
@@ -21,11 +21,13 @@
21
21
  - [best-practice-for-date](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-date)
22
22
  - [best-practice-for-layouts](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts)
23
23
  - [best-practice-for-nested-attributes-array-index](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-nested-attributes-array-index)
24
+ - [best-practice-for-optional-chaining](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-optional-chaining)
24
25
  - [best-practice-for-prohibit-import-smarthr-ui-local](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-prohibit-import-smarthr-ui-local)
25
26
  - [best-practice-for-remote-trigger-dialog](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-remote-trigger-dialog)
26
27
  - [best-practice-for-rest-parameters](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-rest-parameters)
27
28
  - [best-practice-for-tailwind-prohibit-root-margin](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-tailwind-prohibit-root-margin)
28
29
  - [best-practice-for-tailwind-variants](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-tailwind-variants)
30
+ - [best-practice-for-unnesessary-early-return](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-unnesessary-early-return)
29
31
  - [design-system-guideline-prohibit-double-icons](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/design-system-guideline-prohibit-double-icons)
30
32
  - [format-import-path](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/format-import-path)
31
33
  - [format-translate-component](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/format-translate-component)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-smarthr",
3
- "version": "3.4.0",
3
+ "version": "3.5.1",
4
4
  "author": "SmartHR",
5
5
  "license": "MIT",
6
6
  "description": "A sharable ESLint plugin for SmartHR",
@@ -26,7 +26,7 @@
26
26
  "json5": "^2.2.3"
27
27
  },
28
28
  "devDependencies": {
29
- "typescript-eslint": "^8.50.0"
29
+ "typescript-eslint": "^8.50.1"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "eslint": "^9"
@@ -37,5 +37,5 @@
37
37
  "eslintplugin",
38
38
  "smarthr"
39
39
  ],
40
- "gitHead": "25b508c89653cc0e754e091f83d8b31346afb08e"
40
+ "gitHead": "ca09d161140518cc79de3365fab83b93ff883da7"
41
41
  }
@@ -0,0 +1,75 @@
1
+ # smarthr/best-practice-for-optional-chaining
2
+
3
+ - optional chaining(xxx?.yyy記法)を使うことを促すルールです
4
+
5
+ ## rules
6
+
7
+ ```js
8
+ {
9
+ rules: {
10
+ 'smarthr/best-practice-for-optional-chaining': 'error', // 'warn', 'off'
11
+ },
12
+ }
13
+ ```
14
+
15
+ ## ❌ Incorrect
16
+
17
+ ```jsx
18
+ if (action) action()
19
+ ```
20
+
21
+ ```jsx
22
+ if (obj.action) {
23
+ obj.action(hoge, fuga)
24
+ }
25
+ ```
26
+
27
+
28
+ ## ✅ Correct
29
+
30
+ ```jsx
31
+ action?.()
32
+ ```
33
+
34
+ ```jsx
35
+ obj.action?.(hoge, fuga)
36
+ ```
37
+
38
+ ```jsx
39
+ // 実行する関数と無関係の条件の場合は許容
40
+ if (any) {
41
+ action()
42
+ }
43
+ ```
44
+
45
+ ```jsx
46
+ // 実行する関数の存在チェック以外が条件に含まれている場合は許容
47
+ if (action && any) {
48
+ action()
49
+ }
50
+ ```
51
+
52
+ ```jsx
53
+ // if - else などifに他条件が連なる場合は許容
54
+ if (action) {
55
+ action()
56
+ } else {
57
+ ...
58
+ }
59
+ ```
60
+
61
+ ```jsx
62
+ // if内で関数呼び出し以外の処理が存在すれば許容
63
+ if (action) {
64
+ return action()
65
+ }
66
+ ```
67
+
68
+ ```jsx
69
+ // else if内の場合は許容
70
+ if (any) {
71
+ ...
72
+ } else if (action) {
73
+ action()
74
+ }
75
+ ```
@@ -0,0 +1,40 @@
1
+ const SCHEMA = []
2
+
3
+ const REPLACABLE_CALLEE = `[consequent.expression.callee.type='Identifier']`
4
+ const WITHOUT_BODY_IF_ID = `[test.type='Identifier']${REPLACABLE_CALLEE}`
5
+ const WITH_BODY_IF_ID = WITHOUT_BODY_IF_ID.replace(REPLACABLE_CALLEE, "[consequent.body.length=1][consequent.body.0.expression.callee.type='Identifier']")
6
+ const SELECTOR = `IfStatement[alternate=null]:not([parent.type='IfStatement']):matches(${WITHOUT_BODY_IF_ID},${WITHOUT_BODY_IF_ID.replaceAll("'Identifier'", "'MemberExpression'")},${WITH_BODY_IF_ID},${WITH_BODY_IF_ID.replaceAll("'Identifier'", "'MemberExpression'")})`
7
+
8
+
9
+ /**
10
+ * @type {import('@typescript-eslint/utils').TSESLint.RuleModule<''>}
11
+ */
12
+ module.exports = {
13
+ meta: {
14
+ type: 'suggestion',
15
+ fixable: 'code',
16
+ schema: SCHEMA,
17
+ },
18
+ create(context) {
19
+ return {
20
+ [SELECTOR]: (node) => {
21
+ const expression = node.consequent.expression || node.consequent.body[0].expression
22
+ const calleName = context.sourceCode.getText(expression.callee)
23
+
24
+ if (context.sourceCode.getText(node.test) === calleName) {
25
+ context.report({
26
+ node,
27
+ message: `optional chaining(xxx?.yyyy記法)を利用してください
28
+ - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-optional-chaining`,
29
+ fix: (fixer) => fixer.replaceText(
30
+ node,
31
+ context.sourceCode.getText(expression).replace(new RegExp(`^${calleName}\\(`), `${calleName}\?\.(`),
32
+ ),
33
+ })
34
+ }
35
+ },
36
+ }
37
+ },
38
+ }
39
+ module.exports.schema = SCHEMA
40
+
@@ -1,6 +1,6 @@
1
1
  # smarthr/best-practice-for-rest-parameters
2
2
 
3
- - 残余引数(rest parameters)の命名規則を設定するルールです
3
+ - 残余引数(rest parameters)の利用方法を設定するルールです
4
4
  - https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/rest_parameters
5
5
  - 残余引数にはrestという名称を設定することを推奨します
6
6
  - よく利用される `props` などを利用するとエラーになります
@@ -47,9 +47,14 @@ module.exports = {
47
47
  message: `残余引数には ${REST_REGEX} とマッチする名称を指定してください${DETAIL_LINK}`,
48
48
  })
49
49
  },
50
- [`:not(:matches(RestElement,JSXSpreadAttribute,JSXSpreadAttribute>TSAsExpression,SpreadElement,SpreadElement>TSAsExpression,MemberExpression,VariableDeclarator,ArrayExpression,CallExpression,ObjectPattern>Property,ObjectExpression>Property))>Identifier[name=${REST_REGEX}]`]: actionNotRest,
50
+ [`:not(:matches(RestElement,JSXSpreadAttribute,JSXSpreadAttribute>TSAsExpression,SpreadElement,SpreadElement>TSAsExpression,MemberExpression,VariableDeclarator,ArrayExpression,CallExpression,ObjectPattern>Property,ObjectExpression>Property,ReturnStatement,ArrowFunctionExpression))>Identifier[name=${REST_REGEX}]`]: actionNotRest,
51
51
  [`:matches(VariableDeclarator[id.name=${REST_REGEX}],ObjectPattern>Property[value.name=${REST_REGEX}],ObjectExpression>Property[key.name=${REST_REGEX}])`]: actionNotRest,
52
52
  [`MemberExpression[object.name=${REST_REGEX}]`]: actionMemberExpressionName,
53
+ [`ArrowFunctionExpression>Identifier[name=${REST_REGEX}]`]: (node) => {
54
+ if (node !== node.parent.body) {
55
+ actionNotRest(node)
56
+ }
57
+ },
53
58
  [`VariableDeclarator[id.type='ObjectPattern'][init.name=${REST_REGEX}]`]: (node) => {
54
59
  context.report({
55
60
  node,
@@ -0,0 +1,161 @@
1
+ # smarthr/best-practice-for-unnesessary-early-return
2
+
3
+ - 不必要な早期returnをチェックするルールです
4
+ - 不要な早期returnとは **直後に実行される処理の条件を逆転している早期return** を指します
5
+
6
+ ```jsx
7
+ // 不要な早期returnの例
8
+ const anyAction = (a) => {
9
+ if (!a) {
10
+ return
11
+ }
12
+
13
+ otherAction()
14
+ }
15
+ ```
16
+
17
+ - 上記の例の場合、実質 `otherActionを実行する条件だけが存在する` ため、実際にコードを読む際、条件を逆転させて考える余計なコストが発生します
18
+ - 下記の様に早期returnを利用せず書くことを推奨します
19
+
20
+
21
+ ```jsx
22
+ const anyAction = (a) => {
23
+ if (a) {
24
+ otherAction()
25
+ }
26
+ }
27
+ ```
28
+
29
+ - 上記のような条件に修正することで、本質的に実行したい処理の条件がわかりやすくなります
30
+ - このルールは以下の条件すべてに合致する場合、NGにします
31
+ - 早期return以降にifやelse、switch, tryがない
32
+ - 早期returnの条件ブロック以降に変数宣言がない
33
+ - 早期returnの条件ブロック以降にreturnがない
34
+ - 早期returnが値を返していない
35
+
36
+ ## rules
37
+
38
+ ```js
39
+ {
40
+ rules: {
41
+ 'smarthr/best-practice-for-unnesessary-early-return': 'error', // 'warn', 'off'
42
+ },
43
+ }
44
+ ```
45
+
46
+ ## ❌ Incorrect
47
+
48
+ ```jsx
49
+ const anyAction = (a) => {
50
+ // otherActionの実行条件を逆転させているだけのためNG
51
+ if (!a) {
52
+ return
53
+ }
54
+
55
+ otherAction()
56
+ }
57
+ ```
58
+
59
+ ```jsx
60
+ const anyAction = (a, b) => {
61
+ // 早期returnの条件内容自体は無関係にチェックするためNG
62
+ if (!a || !b) {
63
+ return
64
+ }
65
+
66
+ otherAction()
67
+ }
68
+ ```
69
+
70
+ ```jsx
71
+ const anyAction = (a, b) => {
72
+ switch (a) {
73
+ ...
74
+ }
75
+
76
+ // 早期returnより前に条件などがあっても、条件が実質逆転しているためNG
77
+ if (!b) {
78
+ return
79
+ }
80
+
81
+ otherAction()
82
+ }
83
+ ```
84
+
85
+ ```jsx
86
+ const anyAction = (a, b) => {
87
+ if (!a) {
88
+ return
89
+ }
90
+ // 早期returnが分割されているため、前述の早期returnの条件とまとめるべきなのでNG
91
+ if (!b) {
92
+ return
93
+ }
94
+
95
+ otherAction()
96
+ }
97
+ ```
98
+
99
+ ## ✅ Correct
100
+
101
+ ```jsx
102
+ const anyAction = (a) => {
103
+ if (a) {
104
+ otherAction()
105
+ }
106
+ }
107
+ ```
108
+
109
+ ```jsx
110
+ // 許容する早期returnの例
111
+ const sample1 = (a) => {
112
+ if (a) {
113
+ // 早期returnで値を返しているため許容
114
+ return true
115
+ }
116
+ ...
117
+ }
118
+
119
+ const sample2 = (a) => {
120
+ if (!a) {
121
+ return
122
+ }
123
+
124
+ // 早期return後に変数宣言しているため許容
125
+ const caculated = calc(a)
126
+ ...
127
+ }
128
+
129
+ const sample3 = (a) => {
130
+ if (!a) {
131
+ return
132
+ }
133
+ // 早期returnの条件以外に条件が存在するため許容
134
+ else if (a === any) {
135
+ ...
136
+ }
137
+ }
138
+
139
+ const sample4 = (a, b) => {
140
+ if (!a) {
141
+ return
142
+ }
143
+
144
+ otherAction1(a)
145
+
146
+ // この場合も別条件のため許容
147
+ if (b === any) {
148
+ return
149
+ }
150
+ ...
151
+ }
152
+
153
+ const sample5 = (a, b) => {
154
+ if (!a) {
155
+ return
156
+ }
157
+
158
+ // 早期returnとは別のreturnが関数スコープのrootにあるため許容
159
+ return ...
160
+ }
161
+ ```
@@ -0,0 +1,92 @@
1
+ const SCHEMA = []
2
+
3
+ const EARLY_RETURN_IF_STATEMENT = `:matches(ArrowFunctionExpression,FunctionExpression,FunctionDeclaration)>BlockStatement>IfStatement[alternate=null]:matches([consequent.type='ReturnStatement'],[consequent.body.length=1])>`
4
+ const NULL_RETURN_STATEMENT = 'ReturnStatement[argument=null]'
5
+
6
+ const FUNCTION_REGEX = /^(Arrow)?Function(Expression|Declaration)$/
7
+
8
+ const searchFunction = (node) => FUNCTION_REGEX.test(node.type) ? node : searchFunction(node.parent)
9
+
10
+ const getEarlyReturn = (b) => {
11
+ let ret = null
12
+ switch (b.consequent.type) {
13
+ case 'ReturnStatement':
14
+ ret = b.consequent
15
+
16
+ break
17
+ case 'BlockStatement':
18
+ if (b.consequent.body.length === 1 && b.consequent.body[0].type === 'ReturnStatement') {
19
+ ret = b.consequent.body[0]
20
+ }
21
+
22
+ break
23
+ }
24
+
25
+ return ret?.argument === null ? ret : null
26
+ }
27
+
28
+ const DETAIL_LINK = `
29
+ - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-unnesessary-early-return`
30
+
31
+ /**
32
+ * @type {import('@typescript-eslint/utils').TSESLint.RuleModule<''>}
33
+ */
34
+ module.exports = {
35
+ meta: {
36
+ type: 'suggestion',
37
+ schema: SCHEMA,
38
+ },
39
+ create(context) {
40
+ const action = (node) => {
41
+ const fn = searchFunction(node).body.body
42
+ // 0: 最初の早期returnを検索中
43
+ // 1: 最初の早期returnを発見、その後も連続して早期returnが存在する場合
44
+ // 2: 1の後、早期returnの連続が途切れた場合
45
+ let flg = 0
46
+
47
+ for (let i = 0; i < fn.length; i++) {
48
+ const b = fn[i]
49
+
50
+ if (!flg) {
51
+ if (b.type === 'IfStatement' && getEarlyReturn(b)) {
52
+ flg = 1
53
+ }
54
+
55
+ continue
56
+ }
57
+
58
+ switch (b.type) {
59
+ case 'VariableDeclaration':
60
+ case 'ReturnStatement':
61
+ case 'SwitchStatement':
62
+ case 'TryStatement':
63
+ return
64
+ case 'IfStatement':
65
+ if (flg === 1 && node === getEarlyReturn(b)) {
66
+ context.report({
67
+ node,
68
+ message: `早期returnのifが分割されています${DETAIL_LINK}
69
+ - 一つのifにまとめるよう、条件を調整してください`,
70
+ })
71
+ }
72
+ return
73
+ }
74
+
75
+ flg = 2
76
+ }
77
+
78
+ context.report({
79
+ node,
80
+ message: `後続の処理の逆の条件の早期returnのため修正してください。${DETAIL_LINK}
81
+ - 本質的に行いたい処理の条件とは逆がifに記述されているため、ロジックを確認する際条件を逆転させて考える余計な手間が発生しています
82
+ - 条件を逆転させたうえで後続の処理をifの内部に移動してください`,
83
+ })
84
+ }
85
+
86
+ return {
87
+ [`${EARLY_RETURN_IF_STATEMENT}BlockStatement>${NULL_RETURN_STATEMENT}`]: action,
88
+ [`${EARLY_RETURN_IF_STATEMENT}${NULL_RETURN_STATEMENT}`]: action,
89
+ }
90
+ },
91
+ }
92
+ module.exports.schema = SCHEMA
@@ -0,0 +1,48 @@
1
+ const rule = require('../rules/best-practice-for-optional-chaining')
2
+ const RuleTester = require('eslint').RuleTester
3
+
4
+ const ruleTester = new RuleTester({
5
+ languageOptions: {
6
+ parserOptions: {
7
+ ecmaFeatures: {
8
+ jsx: true,
9
+ },
10
+ },
11
+ },
12
+ })
13
+
14
+ const ERROR = `optional chaining(xxx?.yyyy記法)を利用してください
15
+ - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-optional-chaining`
16
+
17
+ ruleTester.run('best-practice-for-optional-chaining', rule, {
18
+ valid: [
19
+ { code: `action?.()` },
20
+ { code: `obj.action?.(hoge, fuga)` },
21
+ { code: `if (any) { action() }` },
22
+ { code: `if (action && any) { action() }` },
23
+ { code: `if (action) { action() } else { any() }` },
24
+ { code: `if (obj.hoge) obj.hoge.fuga.action()` },
25
+ { code: `() => { if (action) return action() }` },
26
+ { code: `if (any) {} else if (action) { action() }` },
27
+ ],
28
+ invalid: [
29
+ { code: `if (action) action()`, errors: [{ message: ERROR }], output: 'action?.()' },
30
+ { code: `if (obj.action) { obj.action(hoge, fuga) }`, errors: [{ message: ERROR }], output: 'obj.action?.(hoge, fuga)' },
31
+ { code: `if (obj.hoge.fuga.action) obj.hoge.fuga.action()`, errors: [{ message: ERROR }], output: 'obj.hoge.fuga.action?.()' },
32
+ { code: `
33
+ if (obj.hoge.fuga.action) {
34
+ obj.hoge.fuga.action(
35
+ a,
36
+ b,
37
+ c
38
+ )
39
+ }
40
+ `, errors: [{ message: ERROR }], output: `
41
+ obj.hoge.fuga.action?.(
42
+ a,
43
+ b,
44
+ c
45
+ )
46
+ ` },
47
+ ],
48
+ })
@@ -32,6 +32,13 @@ ruleTester.run('best-practice-for-rest-parameters', rule, {
32
32
  { code: `const hoge = [rest]` },
33
33
  { code: `hoge(fugaRest)` },
34
34
  { code: `<Any {...rest} />` },
35
+ { code: `
36
+ const removeKey = (key, obj) => {
37
+ const { [key]: _removed, ...rest } = obj
38
+ return rest
39
+ }
40
+ ` },
41
+ { code: `const removeIdAttr = ({ id: _id, ...rest }) => rest` },
35
42
  ],
36
43
  invalid: [
37
44
  { code: `const hoge = ({ ...rest }) => {}`, errors: [ { message: `意味のない残余引数のため、単一の引数に変更してください${DETAIL_LINK}` } ] },
@@ -0,0 +1,189 @@
1
+ const rule = require('../rules/best-practice-for-unnesessary-early-return')
2
+ const RuleTester = require('eslint').RuleTester
3
+
4
+ const ruleTester = new RuleTester({
5
+ languageOptions: {
6
+ parserOptions: {
7
+ ecmaFeatures: {
8
+ jsx: true,
9
+ },
10
+ },
11
+ },
12
+ })
13
+
14
+ const UNNESESSARY_EARLY_RETURN_ERROR = `後続の処理の逆の条件の早期returnのため修正してください。
15
+ - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-unnesessary-early-return
16
+ - 本質的に行いたい処理の条件とは逆がifに記述されているため、ロジックを確認する際条件を逆転させて考える余計な手間が発生しています
17
+ - 条件を逆転させたうえで後続の処理をifの内部に移動してください`
18
+ const SPLIT_EARLY_RETURN_ERROR = `早期returnのifが分割されています
19
+ - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-unnesessary-early-return
20
+ - 一つのifにまとめるよう、条件を調整してください`
21
+
22
+ ruleTester.run('best-practice-for-unnesessary-early-return', rule, {
23
+ valid: [
24
+ { code: `
25
+ const anyAction = (a) => {
26
+ if (a) {
27
+ otherAction()
28
+ }
29
+ }
30
+ ` },
31
+ { code: `
32
+ const sample2 = (a) => {
33
+ if (!a) return
34
+
35
+ const caculated = calc(a)
36
+ }
37
+ ` },
38
+ { code: `
39
+ const anyAction = (a) => {
40
+ if (!a) {
41
+ return
42
+ } else {
43
+ }
44
+ }
45
+ ` },
46
+ { code: `
47
+ const anyAction = (a) => {
48
+ if (!a) {
49
+ return
50
+ } else if (any) {
51
+ }
52
+ }
53
+ ` },
54
+ { code: `
55
+ const sample4 = (a, b) => {
56
+ if (!a) {
57
+ return
58
+ }
59
+
60
+ otherAction1(a)
61
+
62
+ if (b === any) {
63
+ return
64
+ }
65
+
66
+ otherAction2(b)
67
+ }
68
+ ` },
69
+ { code: `
70
+ const sample4 = (a, b) => {
71
+ if (!a) {
72
+ return
73
+ }
74
+
75
+ switch(a) {
76
+ case 'hoge':
77
+ break
78
+ }
79
+ }
80
+ ` },
81
+ { code: `
82
+ const anyAction = (a) => {
83
+ if (!a) {
84
+ otherAction1()
85
+ return
86
+ }
87
+
88
+ otherAction2()
89
+ }
90
+ ` },
91
+ { code: `
92
+ const anyAction = (a) => {
93
+ if (!a) {
94
+ return
95
+ }
96
+
97
+ return otherAction2()
98
+ }
99
+ ` },
100
+ { code: `
101
+ const anyAction = (a) => {
102
+ if (!a) {
103
+ return
104
+ }
105
+
106
+ try {
107
+ otherAction()
108
+ } catch {
109
+ }
110
+ }
111
+ ` },
112
+ ],
113
+ invalid: [
114
+ { code: `
115
+ const anyAction = (a) => {
116
+ if (!a) {
117
+ return
118
+ }
119
+
120
+ otherAction()
121
+ }
122
+ `, errors: [ { message: UNNESESSARY_EARLY_RETURN_ERROR } ] },
123
+ { code: `
124
+ const anyAction = (a) => {
125
+ const hoge = any
126
+
127
+ if (!a) {
128
+ return
129
+ }
130
+
131
+ otherAction()
132
+ }
133
+ `, errors: [ { message: UNNESESSARY_EARLY_RETURN_ERROR } ] },
134
+ { code: `
135
+ const anyAction = (a, b) => {
136
+ if (!a || !b) {
137
+ return
138
+ }
139
+
140
+ otherAction()
141
+ }
142
+ `, errors: [ { message: UNNESESSARY_EARLY_RETURN_ERROR } ] },
143
+ { code: `
144
+ const anyAction = (a, b) => {
145
+ switch (a) {
146
+ case 'hoge':
147
+ break
148
+ }
149
+
150
+ if (!b) {
151
+ return
152
+ }
153
+
154
+ otherAction()
155
+ }
156
+ `, errors: [ { message: UNNESESSARY_EARLY_RETURN_ERROR } ] },
157
+ { code: `
158
+ const anyAction = (a, b) => {
159
+ // 早期returnより前に非早期returnのif,switchがある場合
160
+ if (a && b) {
161
+ return 'hoge'
162
+ }
163
+
164
+ // 早期return後にswitchがある場合強制終了されるが
165
+ // このswitchは早期return前のため後続でエラーになる
166
+ switch (a) {
167
+ case 'hoge':
168
+ break
169
+ }
170
+
171
+ if (!b) {
172
+ return
173
+ }
174
+
175
+ otherAction()
176
+ }
177
+ `, errors: [ { message: UNNESESSARY_EARLY_RETURN_ERROR } ] },
178
+ { code: `
179
+ const anyAction = (a, b) => {
180
+ if (!a) return
181
+ if (!b) {
182
+ return
183
+ }
184
+
185
+ otherAction()
186
+ }
187
+ `, errors: [ SPLIT_EARLY_RETURN_ERROR ] },
188
+ ]
189
+ })