eslint-plugin-smarthr 0.3.19 → 0.3.20

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,8 @@
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
+ ### [0.3.20](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.3.19...v0.3.20) (2024-01-02)
6
+
5
7
  ### [0.3.19](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.3.18...v0.3.19) (2024-01-01)
6
8
 
7
9
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-smarthr",
3
- "version": "0.3.19",
3
+ "version": "0.3.20",
4
4
  "author": "SmartHR",
5
5
  "license": "MIT",
6
6
  "description": "A sharable ESLint plugin for SmartHR",
@@ -36,11 +36,10 @@ const UNEXPECTED_NAMES = {
36
36
  '(Link|^a)$': '(Link)$',
37
37
  }
38
38
 
39
- const INTERACTIVE_COMPONENT_NAMES = Object.keys(EXPECTED_NAMES)
39
+ const INTERACTIVE_COMPONENT_NAMES = Object.keys(EXPECTED_NAMES).join('|')
40
40
  const INTERACTIVE_ON_REGEX = /^on(Change|Input|Focus|Blur|(Double)?Click|Key(Down|Up|Press)|Mouse(Enter|Over|Down|Up|Leave)|Select|Submit)$/
41
41
  const MEANED_ROLE_REGEX = /^(combobox|group|slider|toolbar)$/
42
-
43
- const INTERACTIVE_NODE_TYPE = ['JSXElement', 'JSXExpressionContainer', 'ConditionalExpression']
42
+ const INTERACTIVE_NODE_TYPE_REGEX = /^(JSXElement|JSXExpressionContainer|ConditionalExpression)$/
44
43
 
45
44
  const messageNonInteractiveEventHandler = (nodeName, interactiveComponentRegex, onAttrs) => {
46
45
  const onAttrsText = onAttrs.join(', ')
@@ -48,9 +47,12 @@ const messageNonInteractiveEventHandler = (nodeName, interactiveComponentRegex,
48
47
  return `${nodeName} に${onAttrsText}を設定するとブラウザが正しく解釈が行えず、ユーザーが利用することが出来ない場合があるため、以下のいずれかの対応をおこなってください。
49
48
  - 方法1: ${nodeName}がinput、buttonやaなどのインタラクティブな要素の場合、コンポーネント名の末尾をインタラクティブなコンポーネントであることがわかる名称に変更してください
50
49
  - "${interactiveComponentRegex}" の正規表現にmatchするコンポーネントに差し替える、もしくは名称を変更してください
51
- - 方法2: インタラクティブな親要素、もしくは子要素が存在する場合、直接${onAttrsText}を設定することを検討してください
52
- - 方法3: インタラクティブな親要素、もしくは子要素が存在しない場合、インタラクティブな要素を必ず持つようにマークアップを修正後、${onAttrsText}の設定要素を検討してください
53
- - 方法4: インタラクティブな子要素から発生したイベントをキャッチすることが目的で${onAttrsText}を設定している場合、'role="presentation"' を設定してください
50
+ - 方法2: ${onAttrsText} がコンポーネント内の特定のインタラクティブな要素に設定される場合、名称を具体的なものに変更してください
51
+ - 属性名を"${INTERACTIVE_ON_REGEX}"に一致しないものに変更してください
52
+ - 例: 対象コンポーネント内に '追加ボタン' が存在する場合、'onClick' という属性名を 'onClickAddButton' に変更する
53
+ - 方法3: インタラクティブな親要素、もしくは子要素が存在する場合、直接${onAttrsText}を設定することを検討してください
54
+ - 方法4: インタラクティブな親要素、もしくは子要素が存在しない場合、インタラクティブな要素を必ず持つようにマークアップを修正後、${onAttrsText}の設定要素を検討してください
55
+ - 方法5: インタラクティブな子要素から発生したイベントをキャッチすることが目的で${onAttrsText}を設定している場合、'role="presentation"' を設定してください
54
56
  - 'role="presentation"' を設定した要素はマークアップとしての意味がなくなるため、div・span などマークアップとしての意味を持たない要素に設定してください
55
57
  - 'role="presentation"' を設定する適切な要素が存在しない場合、div、またはspanでイベントが発生する要素を囲んだ上でrole属性を設定してください`
56
58
  }
@@ -81,27 +83,28 @@ module.exports = {
81
83
  },
82
84
  create(context) {
83
85
  const options = context.options[0]
84
- const interactiveComponentRegex = new RegExp(`(${INTERACTIVE_COMPONENT_NAMES.join('|')}${options?.additionalInteractiveComponentRegex ? `|${options.additionalInteractiveComponentRegex.join('|')}` : ''})`)
86
+ const interactiveComponentRegex = new RegExp(`(${INTERACTIVE_COMPONENT_NAMES}${options?.additionalInteractiveComponentRegex ? `|${options.additionalInteractiveComponentRegex.join('|')}` : ''})`)
87
+ const findInteractiveNode = (ec) => ec && ec.type.match(INTERACTIVE_NODE_TYPE_REGEX) && isHasInteractive(ec)
85
88
  const isHasInteractive = (c) => {
86
89
  switch (c.type) {
87
90
  case 'JSXElement': {
88
- if ((c.openingElement.name.name || '').match(interactiveComponentRegex)) {
89
- return true
90
- }
91
+ const name = c.openingElement.name.name
91
92
 
92
- if (c.children.length > 0) {
93
+ if (name && name.match(interactiveComponentRegex)) {
94
+ return true
95
+ } else if (c.children.length > 0) {
93
96
  return !!c.children.find(isHasInteractive)
94
97
  }
95
98
  }
96
99
  case 'JSXExpressionContainer':
97
100
  case 'ConditionalExpression': {
98
- let expression = c
101
+ let e = c
99
102
 
100
103
  if (c.expression) {
101
- expression = c.expression
104
+ e = c.expression
102
105
  }
103
106
 
104
- return !![expression.right, expression.consequent, expression.alternate].find((ec) => INTERACTIVE_NODE_TYPE.includes(ec?.type) && isHasInteractive(ec))
107
+ return !![e.right, e.consequent, e.alternate].find(findInteractiveNode)
105
108
  }
106
109
  }
107
110
 
@@ -127,36 +130,34 @@ module.exports = {
127
130
 
128
131
  if (v === 'presentation') {
129
132
  isMeanedRole = isRolePresentation = true
130
- } else if (v.match(MEANED_ROLE_REGEX)) {
133
+ } else if (v.match(MEANED_ROLE_REGEX)) {
131
134
  isMeanedRole = true
132
135
  }
133
136
  }
134
137
  })
135
138
 
136
- if (!nodeName.match(interactiveComponentRegex)) {
137
- if (onAttrs.length > 0) {
138
- if (!isRolePresentation) {
139
- // HINT: role="presentation"以外で意味があるroleが設定されている場合はエラーにしない
140
- // 基本的にsmarthr-uiでroleの設定などは巻き取る && そもそもroleを設定するよりタグを適切にマークアップすることが優先されるため
141
- // エラーなどには表示しない
142
- if (!isMeanedRole) {
143
- context.report({
144
- node,
145
- message: messageNonInteractiveEventHandler(nodeName, interactiveComponentRegex, onAttrs),
146
- });
147
- }
148
- } else if (!node.parent.children.find(isHasInteractive)) {
149
- context.report({
150
- node,
151
- message: messageRolePresentationNotHasInteractive(nodeName, interactiveComponentRegex, onAttrs)
152
- })
153
- }
139
+ if (nodeName.match(interactiveComponentRegex)) {
140
+ if (isRolePresentation) {
141
+ context.report({
142
+ node,
143
+ message: messageInteractiveHasRolePresentation(nodeName, interactiveComponentRegex)
144
+ })
145
+ }
146
+ } else if (onAttrs.length > 0) {
147
+ // HINT: role="presentation"以外で意味があるroleが設定されている場合はエラーにしない
148
+ // 基本的にsmarthr-uiでroleの設定などは巻き取る && そもそもroleを設定するよりタグを適切にマークアップすることが優先されるため
149
+ // エラーなどには表示しない
150
+ if (!isMeanedRole) {
151
+ context.report({
152
+ node,
153
+ message: messageNonInteractiveEventHandler(nodeName, interactiveComponentRegex, onAttrs),
154
+ });
155
+ } else if (!node.parent.children.find(isHasInteractive)) {
156
+ context.report({
157
+ node,
158
+ message: messageRolePresentationNotHasInteractive(nodeName, interactiveComponentRegex, onAttrs)
159
+ })
154
160
  }
155
- } else if (isRolePresentation) {
156
- context.report({
157
- node,
158
- message: messageInteractiveHasRolePresentation(nodeName, interactiveComponentRegex)
159
- })
160
161
  }
161
162
  },
162
163
  };
@@ -19,9 +19,12 @@ const messageNonInteractiveEventHandler = (nodeName, onAttrs, interactiveCompone
19
19
  return `${nodeName} に${onAttrsText}を設定するとブラウザが正しく解釈が行えず、ユーザーが利用することが出来ない場合があるため、以下のいずれかの対応をおこなってください。
20
20
  - 方法1: ${nodeName}がinput、buttonやaなどのインタラクティブな要素の場合、コンポーネント名の末尾をインタラクティブなコンポーネントであることがわかる名称に変更してください
21
21
  - "${interactiveComponentRegex}" の正規表現にmatchするコンポーネントに差し替える、もしくは名称を変更してください
22
- - 方法2: インタラクティブな親要素、もしくは子要素が存在する場合、直接${onAttrsText}を設定することを検討してください
23
- - 方法3: インタラクティブな親要素、もしくは子要素が存在しない場合、インタラクティブな要素を必ず持つようにマークアップを修正後、${onAttrsText}の設定要素を検討してください
24
- - 方法4: インタラクティブな子要素から発生したイベントをキャッチすることが目的で${onAttrsText}を設定している場合、'role="presentation"' を設定してください
22
+ - 方法2: ${onAttrsText} がコンポーネント内の特定のインタラクティブな要素に設定される場合、名称を具体的なものに変更してください
23
+ - 属性名を"/^on(Change|Input|Focus|Blur|(Double)?Click|Key(Down|Up|Press)|Mouse(Enter|Over|Down|Up|Leave)|Select|Submit)$/"に一致しないものに変更してください
24
+ - 例: 対象コンポーネント内に '追加ボタン' が存在する場合、'onClick' という属性名を 'onClickAddButton' に変更する
25
+ - 方法3: インタラクティブな親要素、もしくは子要素が存在する場合、直接${onAttrsText}を設定することを検討してください
26
+ - 方法4: インタラクティブな親要素、もしくは子要素が存在しない場合、インタラクティブな要素を必ず持つようにマークアップを修正後、${onAttrsText}の設定要素を検討してください
27
+ - 方法5: インタラクティブな子要素から発生したイベントをキャッチすることが目的で${onAttrsText}を設定している場合、'role="presentation"' を設定してください
25
28
  - 'role="presentation"' を設定した要素はマークアップとしての意味がなくなるため、div・span などマークアップとしての意味を持たない要素に設定してください
26
29
  - 'role="presentation"' を設定する適切な要素が存在しない場合、div、またはspanでイベントが発生する要素を囲んだ上でrole属性を設定してください`
27
30
  }