eslint-plugin-smarthr 1.4.2 → 1.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/libs/common_domain.js +5 -8
  3. package/libs/util.js +4 -0
  4. package/package.json +4 -4
  5. package/rules/a11y-anchor-has-href-attribute/index.js +31 -20
  6. package/rules/a11y-clickable-element-has-text/index.js +4 -25
  7. package/rules/a11y-delegate-element-has-role-presentation/index.js +34 -55
  8. package/rules/a11y-form-control-in-form/index.js +5 -31
  9. package/rules/a11y-heading-in-sectioning-content/index.js +7 -63
  10. package/rules/a11y-image-has-alt-attribute/index.js +0 -16
  11. package/rules/a11y-input-has-name-attribute/index.js +15 -33
  12. package/rules/a11y-input-in-form-control/index.js +28 -74
  13. package/rules/a11y-numbered-text-within-ol/index.js +2 -11
  14. package/rules/a11y-prohibit-input-maxlength-attribute/index.js +8 -20
  15. package/rules/a11y-prohibit-input-placeholder/index.js +2 -14
  16. package/rules/a11y-prohibit-sectioning-content-in-form/index.js +5 -37
  17. package/rules/a11y-prohibit-useless-sectioning-fragment/index.js +5 -23
  18. package/rules/a11y-required-layout-as-attribute/index.js +0 -25
  19. package/rules/a11y-trigger-has-button/index.js +10 -18
  20. package/rules/best-practice-for-data-test-attribute/index.js +3 -6
  21. package/rules/best-practice-for-layouts/index.js +2 -16
  22. package/rules/best-practice-for-remote-trigger-dialog/index.js +3 -11
  23. package/rules/best-practice-for-tailwind-prohibit-root-margin/index.js +38 -30
  24. package/rules/best-practice-for-tailwind-variants/index.js +10 -18
  25. package/rules/component-name/README.md +44 -0
  26. package/rules/component-name/index.js +139 -0
  27. package/rules/design-system-guideline-prohibit-double-icons/index.js +1 -11
  28. package/rules/format-import-path/index.js +14 -6
  29. package/rules/format-translate-component/index.js +3 -1
  30. package/rules/no-import-other-domain/index.js +8 -8
  31. package/rules/prohibit-file-name/index.js +1 -1
  32. package/rules/prohibit-import/index.js +21 -23
  33. package/rules/prohibit-path-within-template-literal/index.js +1 -1
  34. package/rules/require-barrel-import/index.js +8 -11
  35. package/rules/require-declaration/index.js +5 -3
  36. package/rules/require-export/index.js +34 -30
  37. package/rules/require-import/index.js +10 -10
  38. package/rules/trim-props/index.js +9 -8
  39. package/test/a11y-anchor-has-href-attribute.js +0 -29
  40. package/test/a11y-clickable-element-has-text.js +0 -66
  41. package/test/{a11y-delegate-element-has-role-presantation.js → a11y-delegate-element-has-role-presentation.js} +3 -2
  42. package/test/a11y-form-control-in-form.js +1 -1
  43. package/test/a11y-heading-in-sectioning-content.js +0 -82
  44. package/test/a11y-image-has-alt-attribute.js +0 -45
  45. package/test/a11y-input-has-name-attribute.js +0 -44
  46. package/test/a11y-input-in-form-control.js +9 -33
  47. package/test/a11y-prohhibit-input-placeholder.js +0 -45
  48. package/test/a11y-trigger-has-button.js +0 -42
  49. package/test/best-practice-for-remote-trigger-dialog.js +0 -6
  50. package/test/component-name.js +247 -0
  51. package/test/prohibit-import.js +13 -13
@@ -2,8 +2,6 @@ const fs = require('fs')
2
2
 
3
3
  const JSON5 = require('json5')
4
4
 
5
- const { generateTagFormatter } = require('../../libs/format_styled_components')
6
-
7
5
  const OPTION = (() => {
8
6
  const file = `${process.cwd()}/package.json`
9
7
 
@@ -19,20 +17,7 @@ const OPTION = (() => {
19
17
  }
20
18
  })()
21
19
 
22
- const EXPECTED_NAMES = {
23
- '(i|I)nput$': 'Input$',
24
- '(t|T)extarea$': 'Textarea$',
25
- '(s|S)elect$': 'Select$',
26
- 'InputFile$': 'InputFile$',
27
- 'RadioButton$': 'RadioButton$',
28
- 'RadioButtonPanel$': 'RadioButtonPanel$',
29
- 'Check(b|B)ox$': 'Checkbox$',
30
- 'Combo(b|B)ox$': 'Combobox$',
31
- '(Date|Wareki)Picker$': '(Date|Wareki)Picker$',
32
- TimePicker$: 'TimePicker$',
33
- DropZone$: 'DropZone$',
34
- }
35
- const TARGET_TAG_NAME_REGEX = new RegExp(`(${Object.keys(EXPECTED_NAMES).join('|')})`)
20
+ const TARGET_TAG_NAME_REGEX = /((I|^i)nput|(T|^t)extarea|(S|^s)elect|InputFile|RadioButton(Panel)?|(Check|Combo)(B|b)ox|(Date|Wareki|Time)Picker|DropZone)$/
36
21
  const INPUT_NAME_REGEX = /^[a-zA-Z0-9_\[\]]+$/
37
22
  const INPUT_TAG_REGEX = /(i|I)nput$/
38
23
  const RADIO_BUTTON_REGEX = /RadioButton(Panel)?$/
@@ -69,12 +54,11 @@ module.exports = {
69
54
  const checkType = option.checkType || 'always'
70
55
 
71
56
  return {
72
- ...generateTagFormatter({ context, EXPECTED_NAMES }),
73
57
  JSXOpeningElement: (node) => {
74
58
  const { name, attributes } = node
75
59
  const nodeName = name.name || ''
76
60
 
77
- if (nodeName.match(TARGET_TAG_NAME_REGEX)) {
61
+ if (TARGET_TAG_NAME_REGEX.test(nodeName)) {
78
62
  let nameAttr = null
79
63
  let hasSpreadAttr = false
80
64
  let hasReactHookFormRegisterSpreadAttr = false
@@ -103,27 +87,25 @@ module.exports = {
103
87
  }
104
88
  })
105
89
 
106
- if (!nameAttr) {
107
- if (
108
- !(OPTION.react_hook_form && hasReactHookFormRegisterSpreadAttr) &&
109
- (attributes.length === 0 || checkType !== 'allow-spread-attributes' || !hasSpreadAttr)
110
- ) {
111
- const isRadio = nodeName.match(RADIO_BUTTON_REGEX) || (nodeName.match(INPUT_TAG_REGEX) && hasRadioInput)
112
-
113
- context.report({
114
- node,
115
- message: `${nodeName} ${isRadio ? MESSAGE_UNDEFINED_FOR_RADIO : MESSAGE_UNDEFINED_FOR_NOT_RADIO}`,
116
- })
117
- }
118
- } else {
119
- const nameValue = nameAttr.value?.value || ''
90
+ if (nameAttr) {
91
+ const nameValue = nameAttr.value?.value
120
92
 
121
- if (nameValue && !nameValue.match(INPUT_NAME_REGEX)) {
93
+ if (nameValue && !INPUT_NAME_REGEX.test(nameValue)) {
122
94
  context.report({
123
95
  node,
124
96
  message: `${nodeName} のname属性の値(${nameValue})${MESSAGE_NAME_FORMAT_SUFFIX}`,
125
97
  })
126
98
  }
99
+ } else if (
100
+ !(OPTION.react_hook_form && hasReactHookFormRegisterSpreadAttr) &&
101
+ (attributes.length === 0 || checkType !== 'allow-spread-attributes' || !hasSpreadAttr)
102
+ ) {
103
+ const isRadio = RADIO_BUTTON_REGEX.test(nodeName) || INPUT_TAG_REGEX.test(nodeName) && hasRadioInput
104
+
105
+ context.report({
106
+ node,
107
+ message: `${nodeName} ${isRadio ? MESSAGE_UNDEFINED_FOR_RADIO : MESSAGE_UNDEFINED_FOR_NOT_RADIO}`,
108
+ })
127
109
  }
128
110
  }
129
111
  },
@@ -1,57 +1,12 @@
1
- const { generateTagFormatter } = require('../../libs/format_styled_components')
2
-
3
- const EXPECTED_LABELED_INPUT_NAMES = {
4
- 'RadioButton$': '(RadioButton)$',
5
- 'RadioButtons$': '(RadioButtons)$',
6
- 'RadioButtonPanel$': '(RadioButtonPanel)$',
7
- 'RadioButtonPanels$': '(RadioButtonPanels)$',
8
- 'Check(b|B)ox$': '(Checkbox)$',
9
- 'Check(b|B)ox(e)?s$': '(Checkboxes)$',
10
- }
11
- const EXPECTED_INPUT_NAMES = {
12
- '(I|^i)nput$': '(Input)$',
13
- 'SearchInput$': '(SearchInput)$',
14
- '(T|^t)extarea$': '(Textarea)$',
15
- '(S|^s)elect$': '(Select)$',
16
- 'InputFile$': '(InputFile)$',
17
- 'Combo(b|B)ox$': '(Combobox)$',
18
- '(Date|Wareki)Picker$': '((Date|Wareki)Picker)$',
19
- 'TimePicker$': '(TimePicker)$',
20
- ...EXPECTED_LABELED_INPUT_NAMES,
21
- }
22
-
23
- const EXPECTED_FORM_CONTROL_NAMES = {
24
- '(FormGroup)$': '(FormGroup)$',
25
- '(FormControl)$': '(FormControl)$',
26
- '((F|^f)ieldset)$': '(Fieldset)$',
27
- }
28
-
29
- const EXPECTED_NAMES = {
30
- ...EXPECTED_INPUT_NAMES,
31
- ...EXPECTED_FORM_CONTROL_NAMES,
32
- '(A|^a)rticle$': '(Article)$',
33
- '(A|^a)side$': '(Aside)$',
34
- '(N|^n)av$': '(Nav)$',
35
- '(S|^s)ection$': '(Section)$',
36
- 'Cluster$': '(Cluster)$',
37
- 'Center$': '(Center)$',
38
- 'Reel$': '(Reel)$',
39
- 'Sidebar$': '(Sidebar)$',
40
- 'Stack$': '(Stack)$',
41
- '(L|^l)abel$': '(Label)$',
42
-
43
- }
44
-
45
- const UNEXPECTED_NAMES = EXPECTED_NAMES
46
-
47
- const FORM_CONTROL_INPUTS_REGEX = new RegExp(`(${Object.keys(EXPECTED_INPUT_NAMES).join('|')})`)
48
- const LABELED_INPUTS_REGEX = new RegExp(`(${Object.keys(EXPECTED_LABELED_INPUT_NAMES).join('|')})`)
1
+ const LABELED_INPUTS_REGEX_STR = 'RadioButton(Panel)?(s)?|Check(B|b)ox(es|s)?'
2
+ const LABELED_INPUTS_REGEX = new RegExp(`(${LABELED_INPUTS_REGEX_STR})$`)
3
+ const FORM_CONTROL_INPUTS_REGEX = new RegExp(`(${LABELED_INPUTS_REGEX_STR}|(Search)?(I|^i)nput(File)?|(T|^t)extarea|(S|^s)elect|Combo(B|b)ox|(Date|Wareki|Time)Picker)$`)
49
4
  const SEARCH_INPUT_REGEX = /SearchInput$/
50
5
  const INPUT_REGEX = /(i|I)nput$/
51
6
  const RADIO_BUTTONS_REGEX = /RadioButton(Panel)?(s)?$/
52
7
  const CHECKBOX_REGEX = /Check(B|b)ox(s|es)?$/
53
8
  const SELECT_REGEX = /(S|s)elect(s)?$/
54
- const FROM_CONTROLS_REGEX = new RegExp(`(${Object.keys(EXPECTED_FORM_CONTROL_NAMES).join('|')})`)
9
+ const FROM_CONTROLS_REGEX = /(Form(Control|Group)|(F|^f)ieldset)$/
55
10
  const FORM_CONTROL_REGEX = /(Form(Control|Group))$/
56
11
  const FIELDSET_REGEX = /Fieldset$/
57
12
  const DIALOG_REGEX = /Dialog(WithTrigger)?$/
@@ -60,11 +15,12 @@ const BARE_SECTIONING_TAG_REGEX = /^(article|aside|nav|section)$/
60
15
  const LAYOUT_COMPONENT_REGEX = /((C(ent|lust)er)|Reel|Sidebar|Stack)$/
61
16
  const AS_REGEX = /^(as|forwardedAs)$/
62
17
  const SUFFIX_S_REGEX = /s$/
18
+ const az_REGEX = /[a-z]/
63
19
 
64
20
  const IGNORE_INPUT_CHECK_PARENT_TYPE = /^(Program|ExportNamedDeclaration)$/
65
21
 
66
22
  const findRoleGroup = (a) => a.name?.name === 'role' && a.value.value === 'group'
67
- const findAsSectioning = (a) => a.name?.name.match(AS_REGEX) && a.value.value.match(BARE_SECTIONING_TAG_REGEX)
23
+ const findAsSectioning = (a) => AS_REGEX.test(a.name?.name) && BARE_SECTIONING_TAG_REGEX.test(a.value.value)
68
24
  const findTitle = (i) => i.key.name === 'title'
69
25
 
70
26
  const SCHEMA = [
@@ -91,18 +47,17 @@ module.exports = {
91
47
  const additionalInputComponents = option.additionalInputComponents?.length > 0 ? new RegExp(`(${option.additionalInputComponents.join('|')})`) : null
92
48
  const additionalMultiInputComponents = option.additionalMultiInputComponents?.length > 0 ? new RegExp(`(${option.additionalMultiInputComponents.join('|')})`) : null
93
49
 
94
- const checkAdditionalInputComponents = (name) => additionalInputComponents && name.match(additionalInputComponents)
95
- const checkAdditionalMultiInputComponents = (name) => additionalMultiInputComponents && name.match(additionalMultiInputComponents)
50
+ const checkAdditionalInputComponents = (name) => additionalInputComponents && additionalInputComponents.test(name)
51
+ const checkAdditionalMultiInputComponents = (name) => additionalMultiInputComponents && additionalMultiInputComponents.test(name)
96
52
 
97
53
  let formControls = []
98
54
  let conditionalformControls = []
99
55
  let checkboxFormControls = []
100
56
 
101
57
  return {
102
- ...generateTagFormatter({ context, EXPECTED_NAMES, UNEXPECTED_NAMES }),
103
58
  JSXOpeningElement: (node) => {
104
59
  const nodeName = node.name.name || '';
105
- const isFormControlInput = nodeName.match(FORM_CONTROL_INPUTS_REGEX)
60
+ const isFormControlInput = FORM_CONTROL_INPUTS_REGEX.test(nodeName)
106
61
  const isAdditionalMultiInput = checkAdditionalMultiInputComponents(nodeName)
107
62
  let conditionalExpressions = []
108
63
 
@@ -110,11 +65,11 @@ module.exports = {
110
65
  let isInMap = false
111
66
 
112
67
  // HINT: 検索ボックスの場合、UIの関係上labelを設定出来ないことが多い & smarthr-ui/SearchInputはa11y対策してあるため無視
113
- if (nodeName.match(SEARCH_INPUT_REGEX)) {
68
+ if (SEARCH_INPUT_REGEX.test(nodeName)) {
114
69
  return
115
70
  }
116
71
 
117
- const isPureInput = nodeName.match(INPUT_REGEX)
72
+ const isPureInput = INPUT_REGEX.test(nodeName)
118
73
  let isPseudoLabel = false
119
74
  let isTypeRadio = false
120
75
  let isTypeCheck = false
@@ -154,9 +109,9 @@ module.exports = {
154
109
  }
155
110
  }
156
111
 
157
- const isPreMultiple = isAdditionalMultiInput || isFormControlInput && nodeName.match(SUFFIX_S_REGEX)
158
- const isRadio = (isPureInput && isTypeRadio) || nodeName.match(RADIO_BUTTONS_REGEX);
159
- const isCheckbox = !isRadio && (isPureInput && isTypeCheck || nodeName.match(CHECKBOX_REGEX));
112
+ const isPreMultiple = isAdditionalMultiInput || isFormControlInput && SUFFIX_S_REGEX.test(nodeName)
113
+ const isRadio = (isPureInput && isTypeRadio) || RADIO_BUTTONS_REGEX.test(nodeName)
114
+ const isCheckbox = !isRadio && (isPureInput && isTypeCheck || CHECKBOX_REGEX.test(nodeName))
160
115
 
161
116
  const wrapComponentName = isRadio ? 'Fieldset' : 'FormControl'
162
117
  const search = (n) => {
@@ -166,7 +121,7 @@ module.exports = {
166
121
  const name = openingElement.name.name
167
122
 
168
123
  if (name) {
169
- if (name.match(FROM_CONTROLS_REGEX)) {
124
+ if (FROM_CONTROLS_REGEX.test(name)) {
170
125
  const hit = formControls.includes(n)
171
126
  let conditionalHit = false
172
127
 
@@ -240,7 +195,7 @@ module.exports = {
240
195
  // HINT: 擬似的にラベルが設定されている場合、無視する
241
196
  if (!isCheckbox && !isPseudoLabel) {
242
197
  const actualName = isSection ? name : `<${name} ${layoutSectionAttribute.name.name}="${layoutSectionAttribute.value.value}">`
243
- const isSelect = !isRadio && nodeName.match(SELECT_REGEX)
198
+ const isSelect = !isRadio && SELECT_REGEX.test(nodeName)
244
199
 
245
200
  context.report({
246
201
  node,
@@ -266,11 +221,11 @@ module.exports = {
266
221
  break
267
222
  }
268
223
  case 'VariableDeclarator': {
269
- if (n.parent.parent?.type && n.parent.parent.type.match(IGNORE_INPUT_CHECK_PARENT_TYPE)) {
224
+ if (n.parent.parent?.type && IGNORE_INPUT_CHECK_PARENT_TYPE.test(n.parent.parent.type)) {
270
225
  const name = n.id.name
271
226
 
272
227
  // 入力要素系コンポーネントの拡張なので対象外
273
- if (name.match(FORM_CONTROL_INPUTS_REGEX) || checkAdditionalMultiInputComponents(name) || checkAdditionalInputComponents(name)) {
228
+ if (FORM_CONTROL_INPUTS_REGEX.test(name) || checkAdditionalMultiInputComponents(name) || checkAdditionalInputComponents(name)) {
274
229
  return
275
230
  }
276
231
  }
@@ -278,11 +233,11 @@ module.exports = {
278
233
  break
279
234
  }
280
235
  case 'FunctionDeclaration': {
281
- if (n.parent.type.match(IGNORE_INPUT_CHECK_PARENT_TYPE)) {
236
+ if (IGNORE_INPUT_CHECK_PARENT_TYPE.test(n.parent.type)) {
282
237
  const name = n.id.name
283
238
 
284
239
  // 入力要素系コンポーネントの拡張なので対象外
285
- if (name.match(FORM_CONTROL_INPUTS_REGEX) || checkAdditionalMultiInputComponents(name) || checkAdditionalInputComponents(name)) {
240
+ if (FORM_CONTROL_INPUTS_REGEX.test(name) || checkAdditionalMultiInputComponents(name) || checkAdditionalInputComponents(name)) {
286
241
  return
287
242
  }
288
243
  }
@@ -291,7 +246,7 @@ module.exports = {
291
246
  // HINT: smarthr-ui/Checkboxはlabelを単独で持つため、FormControl系でラップをする必要はない
292
247
  // HINT: 擬似的にラベルが設定されている場合、無視する
293
248
  if (!isCheckbox && !isPseudoLabel) {
294
- const isSelect = !isRadio && nodeName.match(SELECT_REGEX)
249
+ const isSelect = !isRadio && SELECT_REGEX.test(nodeName)
295
250
 
296
251
  context.report({
297
252
  node,
@@ -328,7 +283,7 @@ module.exports = {
328
283
 
329
284
  if (!nodeName.match(FORM_CONTROL_REGEX) && isRoleGrouop) {
330
285
  const component = formControlMatcher[1]
331
- const actualComponent = component[0].match(/[a-z]/) ? component : `smarthr-ui/${component}`
286
+ const actualComponent = az_REGEX.test(component[0]) ? component : `smarthr-ui/${component}`
332
287
 
333
288
  const message = `${nodeName}に 'role="group" が設定されています。${actualComponent} をつかってマークアップする場合、'role="group"' は不要です
334
289
  - ${nodeName} が ${actualComponent}、もしくはそれを拡張しているコンポーネントではない場合、名称を ${FROM_CONTROLS_REGEX} にマッチしないものに変更してください`
@@ -346,7 +301,7 @@ module.exports = {
346
301
  const name = n.openingElement.name.name || ''
347
302
 
348
303
  // Fieldset > Dialog > Fieldset のようにDialogを挟んだFormControl系のネストは許容する(Portalで実際にはネストしていないため)
349
- if (name.match(DIALOG_REGEX)) {
304
+ if (DIALOG_REGEX.test(name)) {
350
305
  return
351
306
  }
352
307
 
@@ -354,7 +309,7 @@ module.exports = {
354
309
  if (matcher) {
355
310
  // FormControl > FormControl や FormControl > Fieldset のように複数のFormControl系コンポーネントがネストしてしまっているためエラーにする
356
311
  // Fieldset > Fieldset や Fieldset > FormControl のようにFieldsetが親の場合は許容する
357
- if (name.match(FORM_CONTROL_REGEX)) {
312
+ if (FORM_CONTROL_REGEX.test(name)) {
358
313
  context.report({
359
314
  node: n,
360
315
  message: `${name} が、${nodeName} を子要素として持っており、マークアップとして正しくない状態になっています。以下のいずれかの方法で修正を試みてください。
@@ -379,7 +334,7 @@ module.exports = {
379
334
 
380
335
  searchParent(node.parent.parent)
381
336
 
382
- if (!node.selfClosing && nodeName.match(FORM_CONTROL_REGEX) && isRoleGrouop) {
337
+ if (!node.selfClosing && isRoleGrouop && FORM_CONTROL_REGEX.test(nodeName)) {
383
338
  const searchChildren = (n, count = 0) => {
384
339
  switch (n.type) {
385
340
  case 'BinaryExpression':
@@ -432,12 +387,11 @@ module.exports = {
432
387
  case 'JSXElement': {
433
388
  const name = n.openingElement.name.name || ''
434
389
 
435
- if (name.match(FIELDSET_REGEX) || checkAdditionalMultiInputComponents(name)) {
390
+ if (FIELDSET_REGEX.test(name) || checkAdditionalMultiInputComponents(name)) {
436
391
  // 複数inputが存在する可能性のあるコンポーネントなので無限カウントとする
437
392
  return Infinity
438
393
  }
439
394
 
440
-
441
395
  let nextCount = forInSearchChildren(n.openingElement.attributes, count)
442
396
 
443
397
  if (nextCount > 1) {
@@ -445,8 +399,8 @@ module.exports = {
445
399
  }
446
400
 
447
401
  if (
448
- name.match(FORM_CONTROL_INPUTS_REGEX) ||
449
- name.match(FORM_CONTROL_REGEX) ||
402
+ FORM_CONTROL_INPUTS_REGEX.test(name) ||
403
+ FORM_CONTROL_REGEX.test(name) ||
450
404
  checkAdditionalInputComponents(name)
451
405
  ) {
452
406
  nextCount = nextCount + 1
@@ -1,11 +1,3 @@
1
- const { generateTagFormatter } = require('../../libs/format_styled_components')
2
-
3
- const EXPECTED_NAMES = {
4
- '(Ordered(.*)List|^ol)$': '(Ordered(.*)List)$',
5
- '(S|s)elect$': '(Select)$',
6
- }
7
- const UNEXPECTED_NAMES = EXPECTED_NAMES
8
-
9
1
  const NUMBERED_TEXT_REGEX = /^[\s\n]*(([0-9])([^0-9]{2})[^\s\n]*)/
10
2
  const ORDERED_LIST_REGEX = /(Ordered(.*)List|^ol)$/
11
3
  const SELECT_REGEX = /(S|s)elect$/
@@ -18,12 +10,12 @@ const searchOrderedList = (node) => {
18
10
  if (node.type === 'JSXElement' && node.openingElement.name?.name) {
19
11
  const name = node.openingElement.name.name
20
12
 
21
- if (name.match(SELECT_REGEX)) {
13
+ if (SELECT_REGEX.test(name)) {
22
14
  // HINT: select要素の場合、optionのラベルに連番がついている場合がありえるのでignoreする
23
15
  // 通常と処理を分けるためnullではなく0を返す
24
16
  return 0
25
17
  } else if (
26
- name.match(ORDERED_LIST_REGEX) ||
18
+ ORDERED_LIST_REGEX.test(name) ||
27
19
  node.openingElement.attributes.find(findAsOlAttr)
28
20
  ) {
29
21
  return node.openingElement
@@ -129,7 +121,6 @@ module.exports = {
129
121
  }
130
122
 
131
123
  return {
132
- ...generateTagFormatter({ context, EXPECTED_NAMES, UNEXPECTED_NAMES }),
133
124
  JSXAttribute: (node) => {
134
125
  if (node.value?.value && !IGNORE_ATTRIBUTE_VALUE_REGEX.test(node.value.value)) {
135
126
  checker(node, node.value.value.match(NUMBERED_TEXT_REGEX))
@@ -1,16 +1,9 @@
1
- const { generateTagFormatter } = require('../../libs/format_styled_components')
2
-
3
- const EXPECTED_NAMES = {
4
- '(Input|^input)$': '(Input)$',
5
- '(Textarea|^textarea)$': '(Textarea)$',
6
- }
7
-
8
- const UNEXPECTED_NAMES = EXPECTED_NAMES
9
-
10
- const INPUT_COMPONENT_NAMES = new RegExp(`(${Object.keys(EXPECTED_NAMES).join('|')})`)
1
+ const INPUT_COMPONENT_NAMES = /((I|^i)nput|(T|^t)extarea)$/
11
2
 
12
3
  const SCHEMA = []
13
4
 
5
+ const checkHasMaxLength = (attr) => attr.name?.name === 'maxLength'
6
+
14
7
  /**
15
8
  * @type {import('@typescript-eslint/utils').TSESLint.RuleModule<''>}
16
9
  */
@@ -21,21 +14,16 @@ module.exports = {
21
14
  },
22
15
  create(context) {
23
16
  return {
24
- ...generateTagFormatter({ context, EXPECTED_NAMES, UNEXPECTED_NAMES }),
25
17
  JSXOpeningElement: (node) => {
26
- if (node.name.type === 'JSXIdentifier' && INPUT_COMPONENT_NAMES.test(node.name.name)) {
27
- const checkHasMaxLength = (attr) => attr.name?.name === 'maxLength'
28
- const maxLengthAttr = node.attributes.find(checkHasMaxLength)
29
- if (maxLengthAttr) {
30
- context.report({
31
- node,
32
- message: `${node.name.name}にmaxLength属性を設定しないでください。
18
+ if (node.name.type === 'JSXIdentifier' && INPUT_COMPONENT_NAMES.test(node.name.name) && node.attributes.find(checkHasMaxLength)) {
19
+ context.report({
20
+ node,
21
+ message: `${node.name.name}にmaxLength属性を設定しないでください。
33
22
  - maxLength属性がついた要素に、テキストをペーストすると、maxLength属性の値を超えた範囲が意図せず切り捨てられてしまう場合があります
34
23
  - 以下のいずれかの方法で修正をおこなってください
35
24
  - 方法1: pattern属性とtitle属性を組み合わせ、form要素でラップする
36
25
  - 方法2: JavaScriptを用いたバリデーションを実装する`,
37
- })
38
- }
26
+ })
39
27
  }
40
28
  },
41
29
  }
@@ -1,17 +1,6 @@
1
- const { generateTagFormatter } = require('../../libs/format_styled_components')
2
-
3
- const EXPECTED_NAMES = {
4
- '(i|I)nput$': 'Input$',
5
- 'SearchInput$': 'SearchInput$',
6
- '(t|T)extarea$': 'Textarea$',
7
- 'FieldSet$': 'FieldSet$',
8
- 'Combo(b|B)ox$': 'Combobox$',
9
- '(Date|Wareki)Picker$': '(Date|Wareki)Picker$',
10
- 'TimePicker$': 'TimePicker$',
11
- }
12
- const INPUT_TAG_REGEX = /((i|I)nput|(t|T)extarea|FieldSet|Combo(b|B)ox|(Date|Wareki|Time)Picker)$/
1
+ const INPUT_TAG_REGEX = /((I|^i)nput|(T|^t)extarea|FieldSet|Combo(B|b)ox|(Date|Wareki|Time)Picker)$/
13
2
  const SEARCH_INPUT_REGEX = /SearchInput$/
14
- const COMBOBOX_REGEX = /Combo(b|B)ox$/
3
+ const COMBOBOX_REGEX = /Combo(B|b)ox$/
15
4
 
16
5
  /**
17
6
  * @type {import('@typescript-eslint/utils').TSESLint.RuleModule<''>}
@@ -23,7 +12,6 @@ module.exports = {
23
12
  },
24
13
  create(context) {
25
14
  return {
26
- ...generateTagFormatter({ context, EXPECTED_NAMES }),
27
15
  JSXOpeningElement: (node) => {
28
16
  const name = node.name.name
29
17
 
@@ -1,37 +1,4 @@
1
1
  const { rootPath } = require('../../libs/common')
2
- const { generateTagFormatter } = require('../../libs/format_styled_components')
3
-
4
- const SECTIONING_CONTENT_EXPECTED_NAMES = {
5
- '(A|^a)rticle$': '(Article)$',
6
- '(A|^a)side$': '(Aside)$',
7
- '(N|^n)av$': '(Nav)$',
8
- '(S|^s)ection$': '(Section)$',
9
- }
10
- const FIELDSET_EXPECTED_NAMES = {
11
- '(FormControl)$': '(FormControl)$',
12
- '(FormControls)$': '(FormControls)$',
13
- '((F|^f)ieldset)$': '(Fieldset)$',
14
- '(Fieldsets)$': '(Fieldsets)$',
15
- }
16
- const FORM_EXPECTED_NAMES = {
17
- '((F|^f)orm)$': '(Form)$',
18
- '(FormDialog)$': '(FormDialog)$',
19
- 'RemoteTrigger(.*)FormDialog$': '(RemoteTrigger(.*)FormDialog)$',
20
- }
21
-
22
- const WRAPPER_EXPECTED_NAMES = {
23
- ...FIELDSET_EXPECTED_NAMES,
24
- ...FORM_EXPECTED_NAMES,
25
- }
26
-
27
- const EXPECTED_NAMES = {
28
- ...SECTIONING_CONTENT_EXPECTED_NAMES,
29
- ...WRAPPER_EXPECTED_NAMES,
30
- 'SideNav$': '(SideNav)$',
31
- 'IndexNav$': '(IndexNav)$',
32
- }
33
-
34
- const UNEXPECTED_NAMES = EXPECTED_NAMES
35
2
 
36
3
  const asRegex = /^(as|forwardedAs)$/
37
4
  const asFormRegex = /^(form|fieldset)$/
@@ -41,9 +8,11 @@ const includeAsAttrFormOrFieldset = (a) => a.name?.name.match(asRegex) && asForm
41
8
  const includeAsAttrSectioningContent = (a) => a.name?.name.match(asRegex) && asSectioningContentRegex.test(a.value.value)
42
9
  const includeWrapper = (fn) => wrapperRegex.test(fn)
43
10
 
44
- const sectioningContentRegex = new RegExp(`(${Object.keys(SECTIONING_CONTENT_EXPECTED_NAMES).join('|')})`)
45
- const wrapperRegex = new RegExp(`(${Object.keys(WRAPPER_EXPECTED_NAMES).join('|')})`)
46
- const formControlRegex = new RegExp(`(${Object.keys(FIELDSET_EXPECTED_NAMES).join('|')})`)
11
+ const sectioningContentRegex = /((A|^a)(rticle|side)|(N|^n)av|(S|^s)ection)$/
12
+
13
+ const formControlRegexStr = '(FormControl|(F|^f)ieldset)(s)?'
14
+ const formControlRegex = new RegExp(`${formControlRegexStr}$`)
15
+ const wrapperRegex = new RegExp(`(${formControlRegexStr}|(F|^f)orm(Dialog)?)$`)
47
16
  const extRegex = /\.[a-z0-9]+?$/
48
17
  const ignoreNavRegex = /(Side|Index)Nav$/
49
18
  const formPartCheckParentTypeRegex = /^(Program|ExportNamedDeclaration)$/
@@ -146,7 +115,6 @@ module.exports = {
146
115
  const notified = []
147
116
 
148
117
  return {
149
- ...generateTagFormatter({ context, EXPECTED_NAMES, UNEXPECTED_NAMES }),
150
118
  JSXOpeningElement: (node) => {
151
119
  const elementName = node.name.name
152
120
 
@@ -1,32 +1,15 @@
1
- const { generateTagFormatter } = require('../../libs/format_styled_components')
2
-
3
- const EXPECTED_NAMES = {
4
- 'Article$': '(Article)$',
5
- 'Aside$': '(Aside)$',
6
- 'Nav$': '(Nav)$',
7
- 'Section$': '(Section)$',
8
- 'Center$': '(Center)$',
9
- 'Reel$': '(Reel)$',
10
- 'Sidebar$': '(Sidebar)$',
11
- 'Stack$': '(Stack)$',
12
- 'Base$': '(Base)$',
13
- 'BaseColumn$': '(BaseColumn)$',
14
- }
15
-
16
- const UNEXPECTED_NAMES = EXPECTED_NAMES
17
-
18
1
  const BARE_SECTIONING_TAG_REGEX = /^(article|aside|nav|section)$/
19
2
  const SECTIONING_REGEX = /((A(rticle|side))|Nav|Section)$/
20
- const SECTIONING_FRAGMENT_REGEX = /^SectioningFragment$/
3
+ const SECTIONING_FRAGMENT = 'SectioningFragment'
21
4
  const LAYOUT_REGEX = /((C(ent|lust)er)|Reel|Sidebar|Stack|Base(Column)?)$/
22
5
  const AS_REGEX = /^(as|forwardedAs)$/
23
6
 
24
- const includeSectioningAsAttr = (a) => a.name?.name?.match(AS_REGEX) && a.value.value?.match(BARE_SECTIONING_TAG_REGEX)
7
+ const includeSectioningAsAttr = (a) => AS_REGEX.test(a.name?.name) && BARE_SECTIONING_TAG_REGEX.test(a.value.value)
25
8
 
26
9
  const searchSectioningFragment = (node) => {
27
10
  switch (node.type) {
28
11
  case 'JSXElement':
29
- return node.openingElement.name?.name?.match(SECTIONING_FRAGMENT_REGEX) ? node.openingElement : null
12
+ return SECTIONING_FRAGMENT === node.openingElement.name?.name ? node.openingElement : null
30
13
  case 'Program':
31
14
  return null
32
15
  }
@@ -46,16 +29,15 @@ module.exports = {
46
29
  },
47
30
  create(context) {
48
31
  return {
49
- ...generateTagFormatter({ context, EXPECTED_NAMES, UNEXPECTED_NAMES }),
50
32
  JSXOpeningElement: (node) => {
51
33
  const name = node.name?.name || ''
52
34
  let hit = null
53
35
  let asAttr = null
54
36
 
55
- if (name.match(SECTIONING_REGEX)) {
37
+ if (SECTIONING_REGEX.test(name)) {
56
38
  hit = true
57
39
  } else {
58
- asAttr = name.match(LAYOUT_REGEX) && node.attributes.find(includeSectioningAsAttr)
40
+ asAttr = LAYOUT_REGEX.test(name) && node.attributes.find(includeSectioningAsAttr)
59
41
 
60
42
  if (asAttr) {
61
43
  hit = true
@@ -1,27 +1,3 @@
1
- const { generateTagFormatter } = require('../../libs/format_styled_components')
2
-
3
- const LAYOUT_EXPECTED_NAMES = {
4
- 'Center$': '(Center)$',
5
- 'Cluster$': '(Cluster)$',
6
- 'Reel$': '(Reel)$',
7
- 'Sidebar$': '(Sidebar)$',
8
- 'Stack$': '(Stack)$',
9
- 'Base$': '(Base)$',
10
- 'BaseColumn$': '(BaseColumn)$',
11
- }
12
- const EXPECTED_NAMES = {
13
- ...LAYOUT_EXPECTED_NAMES,
14
- 'PageHeading$': '(PageHeading)$',
15
- 'Heading$': '(Heading)$',
16
- '^h1$': '(PageHeading)$',
17
- '^h(|2|3|4|5|6)$': '(Heading)$',
18
- }
19
-
20
- const UNEXPECTED_NAMES = {
21
- ...LAYOUT_EXPECTED_NAMES,
22
- '(Heading|^h(1|2|3|4|5|6))$': '(Heading)$',
23
- }
24
-
25
1
  const layoutRegex = /((C(ent|lust)er)|Reel|Sidebar|Stack|Base(Column)?)$/
26
2
  const headingRegex = /((^h(1|2|3|4|5|6))|Heading)$/
27
3
  const asRegex = /^(as|forwardedAs)$/
@@ -65,7 +41,6 @@ module.exports = {
65
41
  },
66
42
  create(context) {
67
43
  return {
68
- ...generateTagFormatter({ context, EXPECTED_NAMES, UNEXPECTED_NAMES }),
69
44
  JSXOpeningElement: (node) => {
70
45
  const name = node.name.name || ''
71
46
 
@@ -1,18 +1,11 @@
1
- const { generateTagFormatter } = require('../../libs/format_styled_components')
2
-
3
- const EXPECTED_NAMES = {
4
- 'DropdownTrigger$': 'DropdownTrigger$',
5
- 'DialogTrigger$': 'DialogTrigger$',
6
- '(b|B)utton$': 'Button$',
7
- 'AnchorButton$': 'AnchorButton$',
8
- 'ButtonAnchor$': 'ButtonAnchor$',
9
- 'Anchor$': 'Anchor$',
10
- 'Link$': 'Link$',
11
- '^a$': '(Anchor|Link)$',
12
- }
1
+ const TRIGGER_REGEX = /(Dropdown|Dialog)Trigger$/
2
+ const HELP_DIALOG_TRIGGER_REGEX = /HelpDialogTrigger$/
3
+ const BUTTON_REGEX = /(B|^b)utton$/
4
+ const ANCHOR_BUTTON_REGEX = /AnchorButton$/
5
+ const FALSY_TEXT_REGEX = /^\s*\n+\s*$/
13
6
 
14
7
  const filterFalsyJSXText = (cs) => cs.filter((c) => (
15
- !(c.type === 'JSXText' && c.value.match(/^\s*\n+\s*$/))
8
+ !(c.type === 'JSXText' && FALSY_TEXT_REGEX.test(c.value))
16
9
  ))
17
10
 
18
11
  /**
@@ -25,7 +18,6 @@ module.exports = {
25
18
  },
26
19
  create(context) {
27
20
  return {
28
- ...generateTagFormatter({ context, EXPECTED_NAMES }),
29
21
  JSXElement: (parentNode) => {
30
22
  // HINT: 閉じタグが存在しない === 子が存在しない
31
23
  // 子を持っていない場合はおそらく固定の要素を吐き出すコンポーネントと考えられるため
@@ -40,9 +32,9 @@ module.exports = {
40
32
  return
41
33
  }
42
34
 
43
- const match = node.name.name.match(/(Dropdown|Dialog)Trigger$/)
35
+ const match = node.name.name.match(TRIGGER_REGEX)
44
36
 
45
- if (!match || node.name.name.match(/HelpDialogTrigger$/)) {
37
+ if (!match || HELP_DIALOG_TRIGGER_REGEX.test(node.name.name)) {
46
38
  return
47
39
  }
48
40
 
@@ -65,8 +57,8 @@ module.exports = {
65
57
 
66
58
  if (
67
59
  c.type !== 'JSXElement' ||
68
- !c.openingElement.name.name.match(/(b|B)utton$/) ||
69
- c.openingElement.name.name.match(/AnchorButton$/)
60
+ !BUTTON_REGEX.test(c.openingElement.name.name) ||
61
+ ANCHOR_BUTTON_REGEX.test(c.openingElement.name.name)
70
62
  ) {
71
63
  context.report({
72
64
  node: c,