eslint-plugin-smarthr 0.4.1 → 0.4.2
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 +12 -0
- package/libs/format_styled_components.js +40 -34
- package/package.json +1 -1
- package/rules/a11y-clickable-element-has-text/README.md +10 -5
- package/rules/a11y-clickable-element-has-text/index.js +4 -4
- package/rules/a11y-numbered-text-within-ol/index.js +12 -5
- package/rules/best-practice-for-button-element/README.md +35 -0
- package/rules/best-practice-for-button-element/index.js +43 -0
- package/test/a11y-clickable-element-has-text.js +0 -14
- package/test/a11y-numbered-text-within-ol.js +3 -0
- package/test/best-practice-for-button-element.js +38 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
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.4.2](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.4.1...v0.4.2) (2024-03-03)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* best-practice-for-button-elementを追加 ([#115](https://github.com/kufu/eslint-plugin-smarthr/issues/115)) ([c19ab10](https://github.com/kufu/eslint-plugin-smarthr/commit/c19ab1062d00aa000cfcf9b86535af21b47a0ead))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* a11y-numbered-text-within-ol でwidthの値が誤検知されてしまう場合に対応する ([#117](https://github.com/kufu/eslint-plugin-smarthr/issues/117)) ([3741d54](https://github.com/kufu/eslint-plugin-smarthr/commit/3741d5412f62ac04286e0626e37d2ca3c57c4f60))
|
|
16
|
+
|
|
5
17
|
### [0.4.1](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.4.0...v0.4.1) (2024-02-21)
|
|
6
18
|
|
|
7
19
|
|
|
@@ -18,6 +18,44 @@ const checkImportStyledComponents = (node, context) => {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
const getStyledComponentBaseName = (node) => {
|
|
22
|
+
let base = null
|
|
23
|
+
|
|
24
|
+
if (!node.init) {
|
|
25
|
+
return base
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const tag = node.init.tag || node.init
|
|
29
|
+
|
|
30
|
+
if (tag.object?.name === STYLED_COMPONENTS_METHOD) {
|
|
31
|
+
base = tag.property.name
|
|
32
|
+
} else if (tag.callee) {
|
|
33
|
+
const callee = tag.callee
|
|
34
|
+
|
|
35
|
+
switch (STYLED_COMPONENTS_METHOD) {
|
|
36
|
+
case callee.name: {
|
|
37
|
+
const arg = tag.arguments[0]
|
|
38
|
+
base = arg.name || arg.value
|
|
39
|
+
break
|
|
40
|
+
}
|
|
41
|
+
case callee.callee?.name: {
|
|
42
|
+
const arg = callee.arguments[0]
|
|
43
|
+
base = arg.name || arg.value
|
|
44
|
+
break
|
|
45
|
+
}
|
|
46
|
+
case callee.object?.name:
|
|
47
|
+
base = callee.property.name
|
|
48
|
+
break
|
|
49
|
+
case callee.object?.callee?.name:
|
|
50
|
+
const arg = callee.object.arguments[0]
|
|
51
|
+
base = arg.name || arg.value
|
|
52
|
+
break
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return base
|
|
57
|
+
}
|
|
58
|
+
|
|
21
59
|
const generateTagFormatter = ({ context, EXPECTED_NAMES, UNEXPECTED_NAMES }) => {
|
|
22
60
|
const entriesesTagNames = Object.entries(EXPECTED_NAMES).map(([b, e]) => [ new RegExp(b), new RegExp(e) ])
|
|
23
61
|
const entriesesUnTagNames = UNEXPECTED_NAMES ? Object.entries(UNEXPECTED_NAMES).map(([b, e]) => {
|
|
@@ -52,39 +90,7 @@ const generateTagFormatter = ({ context, EXPECTED_NAMES, UNEXPECTED_NAMES }) =>
|
|
|
52
90
|
}
|
|
53
91
|
},
|
|
54
92
|
VariableDeclarator: (node) => {
|
|
55
|
-
|
|
56
|
-
return
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const tag = node.init.tag || node.init
|
|
60
|
-
|
|
61
|
-
let base = null
|
|
62
|
-
|
|
63
|
-
if (tag.object?.name === STYLED_COMPONENTS_METHOD) {
|
|
64
|
-
base = tag.property.name
|
|
65
|
-
} else if (tag.callee) {
|
|
66
|
-
const callee = tag.callee
|
|
67
|
-
|
|
68
|
-
switch (STYLED_COMPONENTS_METHOD) {
|
|
69
|
-
case callee.name: {
|
|
70
|
-
const arg = tag.arguments[0]
|
|
71
|
-
base = arg.name || arg.value
|
|
72
|
-
break
|
|
73
|
-
}
|
|
74
|
-
case callee.callee?.name: {
|
|
75
|
-
const arg = callee.arguments[0]
|
|
76
|
-
base = arg.name || arg.value
|
|
77
|
-
break
|
|
78
|
-
}
|
|
79
|
-
case callee.object?.name:
|
|
80
|
-
base = callee.property.name
|
|
81
|
-
break
|
|
82
|
-
case callee.object?.callee?.name:
|
|
83
|
-
const arg = callee.object.arguments[0]
|
|
84
|
-
base = arg.name || arg.value
|
|
85
|
-
break
|
|
86
|
-
}
|
|
87
|
-
}
|
|
93
|
+
const base = getStyledComponentBaseName(node)
|
|
88
94
|
|
|
89
95
|
if (base) {
|
|
90
96
|
const extended = node.id.name
|
|
@@ -117,4 +123,4 @@ const generateTagFormatter = ({ context, EXPECTED_NAMES, UNEXPECTED_NAMES }) =>
|
|
|
117
123
|
}
|
|
118
124
|
}
|
|
119
125
|
|
|
120
|
-
module.exports = { generateTagFormatter }
|
|
126
|
+
module.exports = { generateTagFormatter, checkImportStyledComponents, getStyledComponentBaseName }
|
package/package.json
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
# smarthr/a11y-clickable-element-has-text
|
|
2
2
|
|
|
3
|
-
- ButtonやAnchor,Link
|
|
3
|
+
- ButtonやAnchor,Link コンポーネントにテキスト要素が設定されていない場合、スクリーンリーダーで押したものが何だったのかわからない等の問題が発生する可能性を防ぐルールです
|
|
4
|
+
- a要素やbutton要素の中身にtextがあることを担保するルール
|
|
5
|
+
- 画像要素の場合は `visuallyHiddenText`や `alt`等代替テキストを設定する
|
|
6
|
+
- SVGの場合はrole="img" と aria-labelを設定する
|
|
7
|
+
- linkとかanchorのchildrenを含まない要素はチェックしない
|
|
8
|
+
- 例) `<YyyAnchor />`
|
|
4
9
|
|
|
5
10
|
## rules
|
|
6
11
|
|
|
@@ -38,7 +43,7 @@
|
|
|
38
43
|
```
|
|
39
44
|
|
|
40
45
|
```jsx
|
|
41
|
-
<XxxAnchor
|
|
46
|
+
<XxxAnchor>
|
|
42
47
|
<XxxTextYyyy />
|
|
43
48
|
</XxxAnchor>
|
|
44
49
|
```
|
|
@@ -57,7 +62,7 @@
|
|
|
57
62
|
</XxxLink>
|
|
58
63
|
```
|
|
59
64
|
```jsx
|
|
60
|
-
<XxxAnchor
|
|
65
|
+
<XxxAnchor>
|
|
61
66
|
<YyyIcon visuallyHiddenText="hoge" />
|
|
62
67
|
</XxxAnchor>
|
|
63
68
|
```
|
|
@@ -68,11 +73,11 @@
|
|
|
68
73
|
```
|
|
69
74
|
|
|
70
75
|
```jsx
|
|
71
|
-
<
|
|
76
|
+
<YyyAnchor />
|
|
72
77
|
```
|
|
73
78
|
|
|
74
79
|
```jsx
|
|
75
|
-
<XxxAnchor
|
|
80
|
+
<XxxAnchor>
|
|
76
81
|
<XxxText />
|
|
77
82
|
</XxxAnchor>
|
|
78
83
|
```
|
|
@@ -30,9 +30,9 @@ const REGEX_NLSP = /^\s*\n+\s*$/
|
|
|
30
30
|
const REGEX_CLICKABLE_ELEMENT = /^(a|(.*?)Anchor(Button)?|(.*?)Link|(b|B)utton)$/
|
|
31
31
|
const REGEX_SMARTHR_LOGO = /SmartHRLogo$/
|
|
32
32
|
const REGEX_TEXT_COMPONENT = /(Text|Message)$/
|
|
33
|
+
const REGEX_JSX_TYPE = /^(JSXText|JSXExpressionContainer)$/
|
|
33
34
|
|
|
34
|
-
const
|
|
35
|
-
const HIT_TEXT_ATTRS = ['visuallyHiddenText', 'alt']
|
|
35
|
+
const HIT_TEXT_ATTR = 'alt'
|
|
36
36
|
|
|
37
37
|
const filterFalsyJSXText = (cs) => cs.filter(checkFalsyJSXText)
|
|
38
38
|
const checkFalsyJSXText = (c) => (
|
|
@@ -68,7 +68,7 @@ module.exports = {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
const recursiveSearch = (c) => {
|
|
71
|
-
if (
|
|
71
|
+
if (REGEX_JSX_TYPE.test(c.type)) {
|
|
72
72
|
return true
|
|
73
73
|
}
|
|
74
74
|
|
|
@@ -97,7 +97,7 @@ module.exports = {
|
|
|
97
97
|
|
|
98
98
|
if (
|
|
99
99
|
prev ||
|
|
100
|
-
|
|
100
|
+
HIT_TEXT_ATTR !== a.name.name
|
|
101
101
|
) {
|
|
102
102
|
return prev
|
|
103
103
|
}
|
|
@@ -6,20 +6,27 @@ const EXPECTED_NAMES = {
|
|
|
6
6
|
}
|
|
7
7
|
const UNEXPECTED_NAMES = EXPECTED_NAMES
|
|
8
8
|
|
|
9
|
-
const NUMBERED_TEXT_REGEX = /^[\s\n]*(([
|
|
9
|
+
const NUMBERED_TEXT_REGEX = /^[\s\n]*(([0-9])([^0-9]{2})[^\s\n]*)/
|
|
10
10
|
const ORDERED_LIST_REGEX = /(Ordered(.*)List|^ol)$/
|
|
11
11
|
const SELECT_REGEX = /(S|s)elect$/
|
|
12
|
+
const IGNORE_ATTRIBUTE_REGEX = /((w|W)idth|(h|H)eight)$/
|
|
13
|
+
const AS_ATTRIBUTE_REGEX = /^(as|forwardedAs)$/
|
|
14
|
+
|
|
15
|
+
const findAsOlAttr = (a) => a.type === 'JSXAttribute' && AS_ATTRIBUTE_REGEX.test(a.name?.name) && a.value?.value === 'ol'
|
|
12
16
|
|
|
13
17
|
const searchOrderedList = (node) => {
|
|
14
18
|
if (node.type === 'JSXElement' && node.openingElement.name?.name) {
|
|
15
19
|
const name = node.openingElement.name.name
|
|
16
20
|
|
|
17
|
-
if (name.match(
|
|
18
|
-
return node.openingElement
|
|
19
|
-
} else if (name.match(SELECT_REGEX)) {
|
|
21
|
+
if (name.match(SELECT_REGEX)) {
|
|
20
22
|
// HINT: select要素の場合、optionのラベルに連番がついている場合がありえるのでignoreする
|
|
21
23
|
// 通常と処理を分けるためnullではなく0を返す
|
|
22
24
|
return 0
|
|
25
|
+
} else if (
|
|
26
|
+
name.match(ORDERED_LIST_REGEX) ||
|
|
27
|
+
node.openingElement.attributes.find(findAsOlAttr)
|
|
28
|
+
) {
|
|
29
|
+
return node.openingElement
|
|
23
30
|
}
|
|
24
31
|
}
|
|
25
32
|
|
|
@@ -121,7 +128,7 @@ module.exports = {
|
|
|
121
128
|
return {
|
|
122
129
|
...generateTagFormatter({ context, EXPECTED_NAMES, UNEXPECTED_NAMES }),
|
|
123
130
|
JSXAttribute: (node) => {
|
|
124
|
-
if (node.value?.value) {
|
|
131
|
+
if (node.value?.value && !IGNORE_ATTRIBUTE_REGEX.test(node.name?.name)) {
|
|
125
132
|
checker(node, node.value.value.match(NUMBERED_TEXT_REGEX))
|
|
126
133
|
}
|
|
127
134
|
},
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# smarthr/best-practice-for-button-element
|
|
2
|
+
|
|
3
|
+
- button要素の利用を禁止し、smarthr/Button、もしくはsmarthr/UnstyledButtonの利用を促すルールです
|
|
4
|
+
- button要素のtype属性のデフォルトは 'submit' であり、これはform要素にbuttonを設置すると、clickでformをsubmitする状態になります
|
|
5
|
+
- 上記挙動は開発者が意図しづらいため、smarthr/Button, smarthr/UnstyledButtonはtype属性のデフォルトを 'button' に変更しています
|
|
6
|
+
|
|
7
|
+
## rules
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
{
|
|
11
|
+
rules: {
|
|
12
|
+
'smarthr/best-practice-for-button-element': 'error', // 'warn', 'off'
|
|
13
|
+
},
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## ❌ Incorrect
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
<button>click</button>
|
|
21
|
+
|
|
22
|
+
const AnyButton = styled.button``
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## ✅ Correct
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
<button type="button">click</button>
|
|
30
|
+
<button type="submit">click</button>
|
|
31
|
+
<Button>click</Button>
|
|
32
|
+
<AnyButton>click</AnyButton>
|
|
33
|
+
|
|
34
|
+
const AnyButton = styled(Button)``
|
|
35
|
+
```
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const { checkImportStyledComponents, getStyledComponentBaseName } = require('../../libs/format_styled_components')
|
|
2
|
+
|
|
3
|
+
const ERRORMESSAGE_SUFFIX = `
|
|
4
|
+
- button要素のtype属性のデフォルトは "submit" のため、button要素がformでラップされていると意図しないsubmitを引き起こす可能性があります
|
|
5
|
+
- smarthr-ui/Button, smarthr-ui/UnstyledButtonのtype属性のデフォルトは "button" になっているため、buttonから置き換えることをおすすめします`
|
|
6
|
+
const ERRORMESSAGE_REQUIRED_TYPE_ATTR = `button要素を利用する場合、type属性に "button" もしくは "submit" を指定してください${ERRORMESSAGE_SUFFIX}`
|
|
7
|
+
const ERRORMESSAGE_PROHIBIT_STYLED = `"styled.button" の直接利用をやめ、smarthr-ui/Button、もしくはsmarthr-ui/UnstyledButtonを利用してください${ERRORMESSAGE_SUFFIX}`
|
|
8
|
+
|
|
9
|
+
const findTypeAttr = (a) => a.type === 'JSXAttribute' && a.name.name === 'type'
|
|
10
|
+
|
|
11
|
+
const SCHEMA = []
|
|
12
|
+
|
|
13
|
+
module.exports = {
|
|
14
|
+
meta: {
|
|
15
|
+
type: 'problem',
|
|
16
|
+
fixable: 'code',
|
|
17
|
+
schema: SCHEMA,
|
|
18
|
+
},
|
|
19
|
+
create(context) {
|
|
20
|
+
return {
|
|
21
|
+
ImportDeclaration: (node) => {
|
|
22
|
+
checkImportStyledComponents(node, context)
|
|
23
|
+
},
|
|
24
|
+
JSXOpeningElement: (node) => {
|
|
25
|
+
if (node.name.name === 'button' && !node.attributes.find(findTypeAttr)) {
|
|
26
|
+
context.report({
|
|
27
|
+
node,
|
|
28
|
+
message: ERRORMESSAGE_REQUIRED_TYPE_ATTR,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
VariableDeclarator: (node) => {
|
|
33
|
+
if (getStyledComponentBaseName(node) === 'button') {
|
|
34
|
+
context.report({
|
|
35
|
+
node,
|
|
36
|
+
message: ERRORMESSAGE_PROHIBIT_STYLED,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
module.exports.schema = SCHEMA
|
|
@@ -92,12 +92,6 @@ ruleTester.run('a11y-clickable-element-has-text', rule, {
|
|
|
92
92
|
{
|
|
93
93
|
code: `<a><img src="hoge.jpg" alt="ほげ" /></a>`,
|
|
94
94
|
},
|
|
95
|
-
{
|
|
96
|
-
code: `<a><Icon visuallyHiddenText="ほげ" /></a>`,
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
code: `<a><AnyComponent><Icon visuallyHiddenText="ほげ" /></AnyComponent></a>`,
|
|
100
|
-
},
|
|
101
95
|
{
|
|
102
96
|
code: `<a>{any}</a>`,
|
|
103
97
|
},
|
|
@@ -194,10 +188,6 @@ ruleTester.run('a11y-clickable-element-has-text', rule, {
|
|
|
194
188
|
code: `<a><img src="hoge.jpg" alt="" /></a>`,
|
|
195
189
|
errors: [{ message: defaultErrorMessage }]
|
|
196
190
|
},
|
|
197
|
-
{
|
|
198
|
-
code: `<a><AnyComponent><Icon visuallyHiddenText="" /></AnyComponent></a>`,
|
|
199
|
-
errors: [{ message: defaultErrorMessage }]
|
|
200
|
-
},
|
|
201
191
|
{
|
|
202
192
|
code: `<button><img src="hoge.jpg" /></button>`,
|
|
203
193
|
errors: [{ message: defaultErrorMessage }]
|
|
@@ -214,10 +204,6 @@ ruleTester.run('a11y-clickable-element-has-text', rule, {
|
|
|
214
204
|
code: `<button><img src="hoge.jpg" alt="" /></button>`,
|
|
215
205
|
errors: [{ message: defaultErrorMessage }]
|
|
216
206
|
},
|
|
217
|
-
{
|
|
218
|
-
code: `<button><AnyComponent><Icon visuallyHiddenText="" /></AnyComponent></button>`,
|
|
219
|
-
errors: [{ message: defaultErrorMessage }]
|
|
220
|
-
},
|
|
221
207
|
{
|
|
222
208
|
code: `<button><SmartHRLogoSuffix /></button>`,
|
|
223
209
|
errors: [{ message: defaultErrorMessage }]
|
|
@@ -21,7 +21,10 @@ ruleTester.run('a11y-numbered-text-within-ol', rule, {
|
|
|
21
21
|
{ code: `<><p>2: hoge</p><p>1: hoge</p></>` },
|
|
22
22
|
{ code: `<ol><li>abc</li><li>def</li></ol>` },
|
|
23
23
|
{ code: `<OrderedList><li>abc</li><li>def</li></OrderedList>` },
|
|
24
|
+
{ code: `<Hoge as="ol"><li>abc</li><li>def</li></Hoge>` },
|
|
25
|
+
{ code: `<Hoge forwardedAs="ol"><li>abc</li><li>def</li></Hoge>` },
|
|
24
26
|
{ code: `<Select><optgroup><option value="hoge">1. hoge</option><option value="fuga">2. fuga</option></optgroup></Select>` },
|
|
27
|
+
{ code: `<><Hoge width="100%" /><Hoge height="200%" /><Hoge maxWidth="30px" /></>` },
|
|
25
28
|
],
|
|
26
29
|
invalid: [
|
|
27
30
|
{ code: `<><p>1: hoge</p><p>2: hoge</p></>`, errors: [ { message: `連番を含むテキストがol要素でマークアップされていません。ol要素でマークアップすることで関連する順番に意味のある要素を適切にマークアップできるため以下の方法で修正してください。
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const rule = require('../rules/best-practice-for-button-element')
|
|
2
|
+
const RuleTester = require('eslint').RuleTester
|
|
3
|
+
|
|
4
|
+
const ruleTester = new RuleTester({
|
|
5
|
+
parserOptions: {
|
|
6
|
+
ecmaVersion: 2018,
|
|
7
|
+
ecmaFeatures: {
|
|
8
|
+
experimentalObjectRestSpread: true,
|
|
9
|
+
jsx: true,
|
|
10
|
+
},
|
|
11
|
+
sourceType: 'module',
|
|
12
|
+
},
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
const ERRORMESSAGE_REQUIRED_TYPE_ATTR = `button要素を利用する場合、type属性に "button" もしくは "submit" を指定してください
|
|
16
|
+
- button要素のtype属性のデフォルトは "submit" のため、button要素がformでラップされていると意図しないsubmitを引き起こす可能性があります
|
|
17
|
+
- smarthr-ui/Button, smarthr-ui/UnstyledButtonのtype属性のデフォルトは "button" になっているため、buttonから置き換えることをおすすめします`
|
|
18
|
+
const ERRORMESSAGE_PROHIBIT_STYLED = `"styled.button" の直接利用をやめ、smarthr-ui/Button、もしくはsmarthr-ui/UnstyledButtonを利用してください
|
|
19
|
+
- button要素のtype属性のデフォルトは "submit" のため、button要素がformでラップされていると意図しないsubmitを引き起こす可能性があります
|
|
20
|
+
- smarthr-ui/Button, smarthr-ui/UnstyledButtonのtype属性のデフォルトは "button" になっているため、buttonから置き換えることをおすすめします`
|
|
21
|
+
|
|
22
|
+
ruleTester.run('best-practice-for-button-element', rule, {
|
|
23
|
+
valid: [
|
|
24
|
+
{ code: `import styled from 'styled-components'` },
|
|
25
|
+
{ code: `import styled, { css } from 'styled-components'` },
|
|
26
|
+
{ code: `<Button />` },
|
|
27
|
+
{ code: `<Button>ほげ</Button>` },
|
|
28
|
+
{ code: `<AnyButton>ほげ</AnyButton>` },
|
|
29
|
+
{ code: `<button type="button">any</button>` },
|
|
30
|
+
{ code: `<button type="submit">any</button>` },
|
|
31
|
+
{ code: 'const HogeButton = styled(HogeButton)``' },
|
|
32
|
+
],
|
|
33
|
+
invalid: [
|
|
34
|
+
{ code: `import hoge from 'styled-components'`, errors: [ { message: `styled-components をimportする際は、名称が"styled" となるようにしてください。例: "import styled from 'styled-components'"` } ] },
|
|
35
|
+
{ code: `<button>ほげ</button>`, errors: [ { message: ERRORMESSAGE_REQUIRED_TYPE_ATTR } ] },
|
|
36
|
+
{ code: 'const HogeButton = styled.button``', errors: [ { message: ERRORMESSAGE_PROHIBIT_STYLED } ] },
|
|
37
|
+
]
|
|
38
|
+
})
|