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
package/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
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
+ ## [1.5.1](https://github.com/kufu/tamatebako/compare/eslint-plugin-smarthr-v1.5.0...eslint-plugin-smarthr-v1.5.1) (2025-05-12)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * require-barrel-import のロジックミスを修正する ([#619](https://github.com/kufu/tamatebako/issues/619)) ([60b21f7](https://github.com/kufu/tamatebako/commit/60b21f7949aa21e85b944825f9a55dda380943fa))
11
+
12
+ ## [1.5.0](https://github.com/kufu/tamatebako/compare/eslint-plugin-smarthr-v1.4.2...eslint-plugin-smarthr-v1.5.0) (2025-05-12)
13
+
14
+
15
+ ### Features
16
+
17
+ * 個別のルールで行っていたコンポーネントの命名規則チェックをcomponent-name ルールとして統一する ([#610](https://github.com/kufu/tamatebako/issues/610)) ([a73b7e7](https://github.com/kufu/tamatebako/commit/a73b7e7e305bbc84fe7001ca9ed040c3889550f4))
18
+
5
19
  ## [1.4.2](https://github.com/kufu/tamatebako/compare/eslint-plugin-smarthr-v1.4.1...eslint-plugin-smarthr-v1.4.2) (2025-03-27)
6
20
 
7
21
 
@@ -1,6 +1,7 @@
1
1
  const path = require('path')
2
2
  const fs = require('fs')
3
3
  const { replacePaths, rootPath } = require('./common')
4
+ const { getParentDir } = require('./util')
4
5
 
5
6
  const BASE_SCHEMA_PROPERTIES = {
6
7
  globalModuleDir: { type: 'array', items: { type: 'string' } },
@@ -14,11 +15,7 @@ const calculateDomainContext = (context) => {
14
15
  }
15
16
 
16
17
  const filename = context.getFilename()
17
- const parentDir = (() => {
18
- const dir = filename.split('/')
19
- dir.pop()
20
- return dir.join('/')
21
- })()
18
+ const parentDir = getParentDir(filename)
22
19
  const humanizeParentDir = parentDir.replace(new RegExp(`^${rootPath}/(.+)$`), '$1')
23
20
 
24
21
  return {
@@ -34,7 +31,7 @@ const calculateDomainNode = (calclatedContext, node) => {
34
31
  const importPath = node.source.value
35
32
  const { option, parentDir, humanizeParentDir } = calclatedContext
36
33
 
37
- let replacedPath = importPath
34
+ let replacedPath = importPath
38
35
 
39
36
  if (replacePaths) {
40
37
  const exts = ['.ts', '.tsx', '.js', '.jsx', '']
@@ -75,7 +72,7 @@ const calculateDomainNode = (calclatedContext, node) => {
75
72
  if (
76
73
  !resolvedImportPath ||
77
74
  option.globalModuleDir &&
78
- option.globalModuleDir.some((global) =>
75
+ option.globalModuleDir.some((global) =>
79
76
  !!resolvedImportPath.match(new RegExp(`^${path.resolve(`${process.cwd()}/${global}`)}`))
80
77
  )
81
78
  ) {
@@ -137,4 +134,4 @@ const calculateDomainNode = (calclatedContext, node) => {
137
134
  }
138
135
  }
139
136
 
140
- module.exports = { BASE_SCHEMA_PROPERTIES, calculateDomainContext, calculateDomainNode }
137
+ module.exports = { BASE_SCHEMA_PROPERTIES, calculateDomainContext, calculateDomainNode, getParentDir }
package/libs/util.js ADDED
@@ -0,0 +1,4 @@
1
+ const PARENT_DIR_REGEX = /^(.+)\/.+?$/
2
+ const getParentDir = (filename) => filename.match(PARENT_DIR_REGEX)?.[1] || ''
3
+
4
+ module.exports = { getParentDir }
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "eslint-plugin-smarthr",
3
- "version": "1.4.2",
3
+ "version": "1.5.1",
4
4
  "author": "SmartHR",
5
5
  "license": "MIT",
6
6
  "description": "A sharable ESLint plugin for SmartHR",
7
7
  "main": "index.js",
8
8
  "engines": {
9
- "node": ">=20.19.0"
9
+ "node": ">=22.15.0"
10
10
  },
11
11
  "scripts": {
12
12
  "test": "jest"
@@ -26,7 +26,7 @@
26
26
  "json5": "^2.2.3"
27
27
  },
28
28
  "devDependencies": {
29
- "typescript-eslint": "^8.19.1"
29
+ "typescript-eslint": "^8.32.0"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "eslint": "^9"
@@ -37,5 +37,5 @@
37
37
  "eslintplugin",
38
38
  "smarthr"
39
39
  ],
40
- "gitHead": "63fb01426aa3a021599ac0e224a0f1946ee9081f"
40
+ "gitHead": "e74469a80bae406252eea39f939efa557857bc88"
41
41
  }
@@ -1,8 +1,6 @@
1
1
  const JSON5 = require('json5')
2
2
  const fs = require('fs')
3
3
 
4
- const { generateTagFormatter } = require('../../libs/format_styled_components')
5
-
6
4
  const OPTION = (() => {
7
5
  const file = `${process.cwd()}/package.json`
8
6
 
@@ -12,26 +10,40 @@ const OPTION = (() => {
12
10
 
13
11
  const json = JSON5.parse(fs.readFileSync(file))
14
12
  const dependencies = [
15
- ...Object.keys(json.dependencies || {}),
16
- ...Object.keys(json.devDependencies || {}),
13
+ ...(json.dependencies ? Object.keys(json.dependencies) : []),
14
+ ...(json.devDependencies ? Object.keys(json.devDependencies) : []),
17
15
  ]
18
16
 
19
- return {
20
- nextjs: dependencies.includes('next'),
21
- react_router: dependencies.includes('react-router-dom'),
22
- }
23
- })()
17
+ let nextjs = false
18
+ let react_router = false
19
+ const result = () => ({
20
+ nextjs,
21
+ react_router,
22
+ })
24
23
 
25
- const EXPECTED_NAMES = {
26
- 'Anchor$': 'Anchor$',
27
- 'Link$': 'Link$',
28
- '^a$': '(Anchor|Link)$',
29
- }
24
+ for (let i = 0; i < dependencies.length; i++) {
25
+ switch (dependencies[i]) {
26
+ case 'next':
27
+ nextjs = true
30
28
 
31
- const UNEXPECTED_NAMES = {
32
- '(Anchor|^a)$': '(Anchor)$',
33
- '(Link|^a)$': '(Link)$',
34
- }
29
+ if (react_router) {
30
+ return result()
31
+ }
32
+
33
+ break
34
+ case 'react-router-dom':
35
+ react_router = true
36
+
37
+ if (nextjs) {
38
+ return result()
39
+ }
40
+
41
+ break
42
+ }
43
+ }
44
+
45
+ return result()
46
+ })()
35
47
 
36
48
  const REGEX_TARGET = /(Anchor|Link|^a)$/
37
49
  const check = (node, checkType) => {
@@ -43,7 +55,7 @@ const baseCheck = (node, checkType) => {
43
55
  const nodeName = node.name.name || ''
44
56
 
45
57
  if (
46
- nodeName.match(REGEX_TARGET) &&
58
+ REGEX_TARGET.test(nodeName) &&
47
59
  checkExistAttribute(node, findHrefAttribute) &&
48
60
  (checkType !== 'allow-spread-attributes' || !node.attributes.some(findSpreadAttr))
49
61
  ) {
@@ -106,7 +118,6 @@ module.exports = {
106
118
  const checkType = option.checkType || 'always'
107
119
 
108
120
  return {
109
- ...generateTagFormatter({ context, EXPECTED_NAMES, UNEXPECTED_NAMES }),
110
121
  JSXOpeningElement: (node) => {
111
122
  const nodeName = check(node, checkType)
112
123
 
@@ -1,5 +1,3 @@
1
- const { generateTagFormatter } = require('../../libs/format_styled_components')
2
-
3
1
  const SCHEMA = [
4
2
  {
5
3
  type: 'object',
@@ -10,22 +8,6 @@ const SCHEMA = [
10
8
  }
11
9
  ]
12
10
 
13
- const EXPECTED_NAMES = {
14
- 'SmartHRLogo$': 'SmartHRLogo$',
15
- '(b|B)utton$': 'Button$',
16
- 'Anchor$': 'Anchor$',
17
- 'Link$': 'Link$',
18
- 'Text$': 'Text$',
19
- 'Message$': 'Message$',
20
- '^a$': '(Anchor|Link)$',
21
- }
22
-
23
- const UNEXPECTED_NAMES = {
24
- '(B|^b)utton$': '(Button)$',
25
- '(Anchor|^a)$': '(Anchor)$',
26
- '(Link|^a)$': '(Link)$',
27
- }
28
-
29
11
  const REGEX_NLSP = /^\s*\n+\s*$/
30
12
  const REGEX_CLICKABLE_ELEMENT = /^(a|(.*?)Anchor(Button)?|(.*?)Link|(b|B)utton)$/
31
13
  const REGEX_SMARTHR_LOGO = /SmartHRLogo$/
@@ -33,9 +15,7 @@ const REGEX_TEXT_COMPONENT = /(Text|Message)$/
33
15
  const REGEX_JSX_TYPE = /^(JSXText|JSXExpressionContainer)$/
34
16
 
35
17
  const filterFalsyJSXText = (cs) => cs.filter(checkFalsyJSXText)
36
- const checkFalsyJSXText = (c) => (
37
- !(c.type === 'JSXText' && c.value.match(REGEX_NLSP))
38
- )
18
+ const checkFalsyJSXText = (c) => c.type !== 'JSXText' || !REGEX_NLSP.test(c.value)
39
19
 
40
20
  const message = `a, buttonなどのクリッカブルな要素内にはテキストを設定してください
41
21
  - 要素内にアイコン、画像のみを設置する場合はaltなどの代替テキスト用属性を指定してください
@@ -55,7 +35,6 @@ module.exports = {
55
35
  const componentsWithText = option.componentsWithText || []
56
36
 
57
37
  return {
58
- ...generateTagFormatter({ context, EXPECTED_NAMES, UNEXPECTED_NAMES }),
59
38
  JSXElement: (parentNode) => {
60
39
  // HINT: 閉じタグが存在しない === テキストノードが存在しない
61
40
  if (!parentNode.closingElement) {
@@ -64,7 +43,7 @@ module.exports = {
64
43
 
65
44
  const node = parentNode.openingElement
66
45
 
67
- if (!node.name.name || !node.name.name.match(REGEX_CLICKABLE_ELEMENT)) {
46
+ if (!node.name.name || !REGEX_CLICKABLE_ELEMENT.test(node.name.name)) {
68
47
  return
69
48
  }
70
49
 
@@ -79,13 +58,13 @@ module.exports = {
79
58
  }
80
59
  case 'JSXElement': {
81
60
  // // HINT: SmartHRLogo コンポーネントは内部でaltを持っているため対象外にする
82
- if (c.openingElement.name.name.match(REGEX_SMARTHR_LOGO)) {
61
+ if (REGEX_SMARTHR_LOGO.test(c.openingElement.name.name)) {
83
62
  return true
84
63
  }
85
64
 
86
65
  const tagName = c.openingElement.name.name
87
66
 
88
- if (tagName.match(REGEX_TEXT_COMPONENT) || componentsWithText.includes(tagName)) {
67
+ if (REGEX_TEXT_COMPONENT.test(tagName) || componentsWithText.includes(tagName)) {
89
68
  return true
90
69
  }
91
70
 
@@ -1,47 +1,28 @@
1
- const { generateTagFormatter } = require('../../libs/format_styled_components');
2
-
3
- const EXPECTED_NAMES = {
4
- '(i|I)nput$': 'Input$',
5
- '(t|T)extarea$': 'Textarea$',
6
- '(s|S)elect$': 'Select$',
7
- 'InputFile$': 'InputFile$',
8
- 'RadioButtonPanel$': 'RadioButtonPanel$',
9
- 'Check(b|B)ox$': 'Checkbox$',
10
- 'Combo(b|B)ox$': 'Combobox$',
11
- '(Date|Wareki)Picker$': '(Date|Wareki)Picker$',
12
- 'TimePicker$': 'TimePicker$',
13
- 'DropZone$': 'DropZone$',
14
- 'Switch$': 'Switch$',
15
- 'SegmentedControl$': 'SegmentedControl$',
16
- 'RightFixedNote$': 'RightFixedNote$',
17
- 'FieldSet$': 'FieldSet$',
18
- 'Fieldset$': 'Fieldset$',
19
- 'FormControl$': 'FormControl$',
20
- 'FormGroup$': 'FormGroup$',
21
- '(b|B)utton$': 'Button$',
22
- 'Anchor$': 'Anchor$',
23
- 'Link$': 'Link$',
24
- 'TabItem$': 'TabItem$',
25
- '^a$': '(Anchor|Link)$',
26
-
27
- '(f|F)orm$': 'Form$',
28
- 'ActionDialogWithTrigger$': 'ActionDialogWithTrigger$',
29
- 'RemoteDialogTrigger$': 'RemoteDialogTrigger$',
30
- 'RemoteTrigger(.+)Dialog$': 'RemoteTrigger(.+)Dialog$',
31
- 'FormDialog$': 'FormDialog$',
32
- 'Pagination$': 'Pagination$',
33
- 'SideNav$': 'SideNav$',
34
- 'AccordionPanel$': 'AccordionPanel$',
35
- 'FilterDropdown$': 'FilterDropdown$',
36
- }
37
-
38
- const UNEXPECTED_NAMES = {
39
- '(B|^b)utton$': '(Button)$',
40
- '(Anchor|^a)$': '(Anchor)$',
41
- '(Link|^a)$': '(Link)$',
42
- }
43
-
44
- const INTERACTIVE_COMPONENT_NAMES = Object.keys(EXPECTED_NAMES).join('|')
1
+ const INTERACTIVE_COMPONENT_NAMES = `(${[
2
+ '(B|b)utton',
3
+ '(Check|Combo)(B|b)ox',
4
+ '(Date(timeLocal)?|Time|Month|Wareki)Picker',
5
+ '(I|i)nput(File)?',
6
+ '(S|s)elect',
7
+ '(T|t)extarea',
8
+ '(ActionDialogWith|RemoteDialog)Trigger',
9
+ 'AccordionPanel',
10
+ '^a',
11
+ 'Anchor',
12
+ 'Link',
13
+ 'DropZone',
14
+ 'Field(S|s)et',
15
+ 'FilterDropdown',
16
+ '(F|f)orm(Control|Group|Dialog)?',
17
+ 'Pagination',
18
+ 'RadioButton(Panel)?',
19
+ 'RemoteTrigger(.+)Dialog',
20
+ 'RightFixedNote',
21
+ 'SegmentedControl',
22
+ 'SideNav',
23
+ 'Switch',
24
+ 'TabItem',
25
+ ].join('|')})$`
45
26
  const INTERACTIVE_ON_REGEX = /^on(Change|Input|Focus|Blur|(Double)?Click|Key(Down|Up|Press)|Mouse(Enter|Over|Down|Up|Leave)|Select|Submit)$/
46
27
  const MEANED_ROLE_REGEX = /^(combobox|group|slider|toolbar)$/
47
28
  const INTERACTIVE_NODE_TYPE_REGEX = /^(JSXElement|JSXExpressionContainer|ConditionalExpression)$/
@@ -94,13 +75,13 @@ module.exports = {
94
75
  create(context) {
95
76
  const options = context.options[0]
96
77
  const interactiveComponentRegex = new RegExp(`(${INTERACTIVE_COMPONENT_NAMES}${options?.additionalInteractiveComponentRegex ? `|${options.additionalInteractiveComponentRegex.join('|')}` : ''})`)
97
- const findInteractiveNode = (ec) => ec && ec.type.match(INTERACTIVE_NODE_TYPE_REGEX) && isHasInteractive(ec)
78
+ const findInteractiveNode = (ec) => ec && INTERACTIVE_NODE_TYPE_REGEX.test(ec.type) && isHasInteractive(ec)
98
79
  const isHasInteractive = (c) => {
99
80
  switch (c.type) {
100
81
  case 'JSXElement': {
101
82
  const name = c.openingElement.name.name
102
83
 
103
- if (name && name.match(interactiveComponentRegex)) {
84
+ if (name && interactiveComponentRegex.test(name)) {
104
85
  return true
105
86
  } else if (c.children.length > 0) {
106
87
  return !!c.children.find(isHasInteractive)
@@ -122,7 +103,6 @@ module.exports = {
122
103
  }
123
104
 
124
105
  return {
125
- ...generateTagFormatter({ context, EXPECTED_NAMES, UNEXPECTED_NAMES }),
126
106
  JSXOpeningElement: (node) => {
127
107
  const nodeName = node.name.name || '';
128
108
 
@@ -134,7 +114,7 @@ module.exports = {
134
114
  node.attributes.forEach((a) => {
135
115
  const aName = a.name?.name || ''
136
116
 
137
- if (aName.match(INTERACTIVE_ON_REGEX)) {
117
+ if (INTERACTIVE_ON_REGEX.test(aName)) {
138
118
  onAttrs.push(aName)
139
119
  } else if (AS_REGEX.test(aName) && AS_VALUE_REGEX.test(a.value?.value || '')) {
140
120
  isAsInteractive = true
@@ -144,13 +124,13 @@ module.exports = {
144
124
  if (v === 'presentation') {
145
125
  isRolePresentation = true
146
126
  roleMean = v
147
- } else if (v.match(MEANED_ROLE_REGEX)) {
127
+ } else if (MEANED_ROLE_REGEX.test(v)) {
148
128
  roleMean = v
149
129
  }
150
130
  }
151
131
  })
152
132
 
153
- if (isAsInteractive || nodeName.match(interactiveComponentRegex)) {
133
+ if (isAsInteractive || interactiveComponentRegex.test(nodeName)) {
154
134
  if (isRolePresentation) {
155
135
  context.report({
156
136
  node,
@@ -204,11 +184,10 @@ module.exports = {
204
184
  case 'JSXElement': {
205
185
  const name = n.openingElement.name.name || ''
206
186
 
207
- if (name.match(interactiveComponentRegex)) {
208
- return true
209
- }
210
-
211
- if (forInSearchChildren(n.openingElement.attributes)) {
187
+ if (
188
+ interactiveComponentRegex.test(name) ||
189
+ forInSearchChildren(n.openingElement.attributes)
190
+ ) {
212
191
  return true
213
192
  }
214
193
 
@@ -1,36 +1,11 @@
1
- const { generateTagFormatter } = require('../../libs/format_styled_components')
2
-
3
- const FIELDSET_EXPECTED_NAMES = {
4
- '((F|^f)ieldset)$': '(Fieldset)$',
5
- '(Fieldsets)$': '(Fieldsets)$',
6
- }
7
- const FORM_CONTROL_EXPECTED_NAMES = {
8
- ...FIELDSET_EXPECTED_NAMES,
9
- '(FormGroup)$': '(FormGroup)$',
10
- '(FormControl)$': '(FormControl)$',
11
- '(FormControls)$': '(FormControls)$',
12
- }
13
- const FORM_EXPECTED_NAMES = {
14
- '((F|^f)orm)$': '(Form)$',
15
- '(FormDialog)$': '(FormDialog)$',
16
- 'RemoteTrigger(.*)FormDialog$': 'RemoteTrigger(.*)FormDialog$',
17
- 'FilterDropdown$': '(FilterDropdown)$',
18
- }
19
- const EXPECTED_NAMES = {
20
- ...FORM_CONTROL_EXPECTED_NAMES,
21
- ...FORM_EXPECTED_NAMES,
22
- }
23
- const UNEXPECTED_NAMES = EXPECTED_NAMES
24
-
25
- const targetRegex = new RegExp(`(${Object.keys(FORM_CONTROL_EXPECTED_NAMES).join('|')})`)
26
- const wrapperRegex = new RegExp(`(${Object.keys(EXPECTED_NAMES).join('|')})`)
1
+ const targetRegex = /((F|^f)ieldset|(F|^f)orm(Group|Control))(s)?$/
2
+ const wrapperRegex = /((F|^f)ieldset(s)?|(F|^f)orm((Group|Control)(s)?)?|(RemoteTrigger(.*))?FormDialog|FilterDropdown)$/
27
3
  const ignoreCheckParentTypeRegex = /^(Program|ExportNamedDeclaration)$/
28
- const messageFieldset = `(${Object.values(FORM_CONTROL_EXPECTED_NAMES).join('|')})`
29
- const declaratorTargetRegex = new RegExp(messageFieldset)
4
+ const declaratorTargetRegex = /(Fieldset|Form(Group|Control))(s)?$/
30
5
  const asRegex = /^(as|forwardedAs)$/
31
6
  const bareTagRegex = /^(form|fieldset)$/
32
7
 
33
- const includeAsAttrFormOrFieldset = (a) => a.name?.name.match(asRegex) && a.value.value.match(bareTagRegex)
8
+ const includeAsAttrFormOrFieldset = (a) => asRegex.test(a.name?.name) && bareTagRegex.test(a.value.value)
34
9
 
35
10
  const searchBubbleUp = (node) => {
36
11
  switch (node.type) {
@@ -73,7 +48,6 @@ module.exports = {
73
48
  },
74
49
  create(context) {
75
50
  return {
76
- ...generateTagFormatter({ context, EXPECTED_NAMES, UNEXPECTED_NAMES }),
77
51
  JSXOpeningElement: (node) => {
78
52
  const elementName = node.name.name
79
53
 
@@ -87,7 +61,7 @@ module.exports = {
87
61
  - form要素で囲むことでスクリーンリーダーに入力フォームであることが正しく伝わる、入力要素にfocusした状態でEnterを押せばsubmitできる、inputのpattern属性を利用できるなどのメリットがあります
88
62
  - 以下のいずれかの方法で修正をおこなってください
89
63
  - 方法1: form要素で ${elementName} を囲んでください。smarthr-ui/ActionDialog、もしくはsmarthr-ui/RemoteTriggerActionDialogを利用している場合、smarthr-ui/FormDialog、smarthr-ui/RemoteTriggerFormDialogに置き換えてください
90
- - 方法2: ${elementName} がコンポーネント内の一要素であり、かつその親コンポーネントがFormControl、もしくはFieldsetを表現するものである場合、親コンポーネント名を "${messageFieldset}" とマッチするものに変更してください`,
64
+ - 方法2: ${elementName} がコンポーネント内の一要素であり、かつその親コンポーネントがFormControl、もしくはFieldsetを表現するものである場合、親コンポーネント名を "${declaratorTargetRegex}" とマッチするものに変更してください`,
91
65
  })
92
66
  }
93
67
  }
@@ -1,55 +1,3 @@
1
- const { generateTagFormatter } = require('../../libs/format_styled_components')
2
-
3
- const EXPECTED_NAMES = {
4
- 'PageHeading$': 'PageHeading$',
5
- 'Heading$': 'Heading$',
6
- '^h1$': 'PageHeading$',
7
- '^h(|2|3|4|5|6)$': 'Heading$',
8
- 'Article$': 'Article$',
9
- 'Aside$': 'Aside$',
10
- 'Nav$': 'Nav$',
11
- 'Section$': 'Section$',
12
- 'ModelessDialog$': 'ModelessDialog$',
13
- 'Center$': 'Center$',
14
- 'Cluster$': '(Cluster)$',
15
- 'Reel$': 'Reel$',
16
- 'Sidebar$': 'Sidebar$',
17
- 'Stack$': 'Stack$',
18
- 'Base$': 'Base$',
19
- 'BaseColumn$': 'BaseColumn$',
20
- }
21
-
22
- const unexpectedMessageTemplate = `{{extended}} は smarthr-ui/{{expected}} をextendすることを期待する名称になっています
23
- - childrenにHeadingを含まない場合、コンポーネントの名称から"{{expected}}"を取り除いてください
24
- - childrenにHeadingを含み、アウトラインの範囲を指定するためのコンポーネントならば、smarthr-ui/{{expected}}をexendしてください
25
- - "styled(Xxxx)" 形式の場合、拡張元であるXxxxコンポーネントの名称の末尾に"{{expected}}"を設定し、そのコンポーネント内でsmarthr-ui/{{expected}}を利用してください`
26
- const UNEXPECTED_NAMES = {
27
- '(Heading|^h(1|2|3|4|5|6))$': '(Heading)$',
28
- '(A|^a)rticle$': [
29
- '(Article)$',
30
- unexpectedMessageTemplate,
31
- ],
32
- '(A|^a)side$': [
33
- '(Aside)$',
34
- unexpectedMessageTemplate,
35
- ],
36
- '(N|^n)av$': [
37
- '(Nav)$',
38
- unexpectedMessageTemplate,
39
- ],
40
- '(S|^s)ection$': [
41
- '(Section)$',
42
- unexpectedMessageTemplate,
43
- ],
44
- 'Center$': '(Center)$',
45
- 'Cluster$': '(Cluster)$',
46
- 'Reel$': '(Reel)$',
47
- 'Sidebar$': '(Sidebar)$',
48
- 'Stack$': '(Stack)$',
49
- 'Base$': '(Base)$',
50
- 'BaseColumn$': '(BaseColumn)$',
51
- }
52
-
53
1
  const headingRegex = /((^h(1|2|3|4|5|6))|Heading)$/
54
2
  const pageHeadingRegex = /PageHeading$/
55
3
  const declaratorHeadingRegex = /Heading$/
@@ -63,7 +11,7 @@ const noHeadingTagNamesRegex = /^(span|legend)$/
63
11
  const ignoreHeadingCheckParentTypeRegex = /^(Program|ExportNamedDeclaration)$/
64
12
  const headingAttributeRegex = /^(heading|title)$/
65
13
 
66
- const includeSectioningAsAttr = (a) => a.name?.name.match(asRegex) && bareTagRegex.test(a.value.value)
14
+ const includeSectioningAsAttr = (a) => asRegex.test(a.name?.name) && bareTagRegex.test(a.value.value)
67
15
  const findHeadingAttribute = (a) => headingAttributeRegex.test(a.name?.name || '')
68
16
 
69
17
  const headingMessage = `smarthr-ui/Headingと紐づく内容の範囲(アウトライン)が曖昧になっています。
@@ -117,7 +65,7 @@ const searchBubbleUp = (node) => {
117
65
 
118
66
  if (
119
67
  // Headingコンポーネントの拡張なので対象外
120
- node.type === 'VariableDeclarator' && node.parent.parent?.type.match(ignoreHeadingCheckParentTypeRegex) && declaratorHeadingRegex.test(node.id.name) ||
68
+ node.type === 'VariableDeclarator' && ignoreHeadingCheckParentTypeRegex.test(node.parent.parent?.type) && declaratorHeadingRegex.test(node.id.name) ||
121
69
  node.type === 'FunctionDeclaration' && ignoreHeadingCheckParentTypeRegex.test(node.parent.type) && declaratorHeadingRegex.test(node.id.name) ||
122
70
  // ModelessDialogのheaderにHeadingを設定している場合も対象外
123
71
  node.type === 'JSXAttribute' && node.name.name === 'header' && modelessDialogRegex.test(node.parent.name.name)
@@ -197,7 +145,7 @@ const searchChildren = (n) => {
197
145
  } else if (
198
146
  (
199
147
  headingRegex.test(name) &&
200
- !n.openingElement.attributes.find(findTagAttr)?.value.value?.match(noHeadingTagNamesRegex)
148
+ !noHeadingTagNamesRegex.test(n.openingElement.attributes.find(findTagAttr)?.value.value)
201
149
  ) ||
202
150
  forInSearchChildren(n.openingElement.attributes)
203
151
  ) {
@@ -238,15 +186,11 @@ module.exports = {
238
186
  create(context) {
239
187
  let h1s = []
240
188
  let sections = []
241
- let { VariableDeclarator, ...formatter } = generateTagFormatter({ context, EXPECTED_NAMES, UNEXPECTED_NAMES, unexpectedMessageTemplate })
242
-
243
- formatter.VariableDeclarator = (node) => {
244
- VariableDeclarator(node)
245
- VariableDeclaratorBareToSHR(context, node)
246
- }
247
189
 
248
190
  return {
249
- ...formatter,
191
+ VariableDeclarator: (node) => {
192
+ VariableDeclaratorBareToSHR(context, node)
193
+ },
250
194
  JSXOpeningElement: (node) => {
251
195
  const elementName = node.name.name || ''
252
196
  const message = reportMessageBareToSHR(elementName, false)
@@ -260,7 +204,7 @@ module.exports = {
260
204
  } else if (headingRegex.test(elementName)) {
261
205
  const tagAttr = node.attributes.find(findTagAttr)
262
206
 
263
- if (!tagAttr?.value.value?.match(noHeadingTagNamesRegex)) {
207
+ if (!noHeadingTagNamesRegex.test(tagAttr?.value.value)) {
264
208
  const result = searchBubbleUp(node.parent)
265
209
  let hit = false
266
210
 
@@ -1,18 +1,3 @@
1
- const { generateTagFormatter } = require('../../libs/format_styled_components')
2
-
3
- const EXPECTED_NAMES = {
4
- 'Img$': 'Img$',
5
- 'Image$': 'Image$',
6
- 'Icon$': 'Icon$',
7
- '^(img|svg)$': '(Img|Image|Icon)$',
8
- }
9
-
10
- const UNEXPECTED_NAMES = {
11
- '(Img|^(img|svg))$': '(Img)$',
12
- '(Image|^(img|svg))$': '(Image)$',
13
- '(Icon|^(img|svg))$': '(Icon)$',
14
- }
15
-
16
1
  const REGEX_IMG = /(img|image)$/i // HINT: Iconは別途テキストが存在する場合が多いためチェックの対象外とする
17
2
 
18
3
  const findAltAttr = (a) => a.name?.name === 'alt'
@@ -61,7 +46,6 @@ module.exports = {
61
46
  const checkType = option.checkType || 'always'
62
47
 
63
48
  return {
64
- ...generateTagFormatter({ context, EXPECTED_NAMES, UNEXPECTED_NAMES }),
65
49
  JSXOpeningElement: (node) => {
66
50
  if (node.name.name) {
67
51
  const matcher = node.name.name.match(REGEX_IMG)