eslint-plugin-smarthr 3.0.0 → 3.2.0

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,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
+ ## [3.2.0](https://github.com/kufu/tamatebako/compare/eslint-plugin-smarthr-v3.1.0...eslint-plugin-smarthr-v3.2.0) (2025-12-15)
6
+
7
+
8
+ ### Features
9
+
10
+ * best-practice-for-prohibit-import-smarthr-ui-local を追加 ([#946](https://github.com/kufu/tamatebako/issues/946)) ([1be8bf8](https://github.com/kufu/tamatebako/commit/1be8bf82aa97ccca9dba60277f50c5247ad2b664))
11
+
12
+ ## [3.1.0](https://github.com/kufu/tamatebako/compare/eslint-plugin-smarthr-v3.0.0...eslint-plugin-smarthr-v3.1.0) (2025-12-10)
13
+
14
+
15
+ ### Features
16
+
17
+ * best-practice-for-layoutsにradio, checkに対するチェックを追加 ([#937](https://github.com/kufu/tamatebako/issues/937)) ([97a393a](https://github.com/kufu/tamatebako/commit/97a393a9ae2a4cdfbfe8b82aba8cf15f22991e45))
18
+
5
19
  ## [3.0.0](https://github.com/kufu/tamatebako/compare/eslint-plugin-smarthr-v2.5.0...eslint-plugin-smarthr-v3.0.0) (2025-12-08)
6
20
 
7
21
 
package/README.md CHANGED
@@ -21,6 +21,7 @@
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-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)
24
25
  - [best-practice-for-remote-trigger-dialog](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-remote-trigger-dialog)
25
26
  - [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)
26
27
  - [best-practice-for-tailwind-variants](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-tailwind-variants)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-smarthr",
3
- "version": "3.0.0",
3
+ "version": "3.2.0",
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.46.3"
29
+ "typescript-eslint": "^8.49.0"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "eslint": "^9"
@@ -37,5 +37,5 @@
37
37
  "eslintplugin",
38
38
  "smarthr"
39
39
  ],
40
- "gitHead": "81723cdb62ca80730409d872043a72f7844abc46"
40
+ "gitHead": "c6e4fb54bb2ff5d8421c656213dc29f3bc7237ac"
41
41
  }
@@ -6,8 +6,10 @@ const OPTION = (() => {
6
6
  const file = `${process.cwd()}/package.json`
7
7
 
8
8
  if (fs.existsSync(file)) {
9
+ const packageConfig = JSON5.parse(fs.readFileSync(file)).dependencies
10
+
9
11
  return {
10
- react_hook_form: Object.keys(JSON5.parse(fs.readFileSync(file)).dependencies).includes('react-hook-form'),
12
+ react_hook_form: packageConfig ? Object.keys(packageConfig).includes('react-hook-form') : false,
11
13
  }
12
14
  }
13
15
 
@@ -37,6 +37,11 @@
37
37
  <SubText />
38
38
  </Stack>
39
39
  } />
40
+
41
+ // Checkbox, RadioButtonのchildrenにLayout系コンポーネントを設置する場合、as・forwardedAs属性にspanを指定していないければエラー
42
+ <AnyRadioButton><Cluster><A /><B /></Cluster></AnyRadioButton>
43
+ <RadioButtonPanel><AnyStack><A /><B /></AnyStack></RadioButtonPanel>
44
+ <AnyCheckbox><Sidebar><A /><B /></Sidebar></AnyCheckbox>
40
45
  ```
41
46
 
42
47
  ## ✅ Correct
@@ -94,4 +99,9 @@
94
99
  statusLabels={<RequiredLabel />}
95
100
  subActionArea={<HelpLink />}
96
101
  />
102
+
103
+ // Checkbox, RadioButtonのchildrenにLayout系コンポーネントを設置する場合、as・forwardedAs属性にspanを指定する
104
+ <AnyRadioButton><Cluster as="span"><A /><B /></Cluster></AnyRadioButton>
105
+ <RadioButtonPanel><AnyStack forwardedAs="span"><A /><B /></AnyStack></RadioButtonPanel>
106
+ <AnyCheckbox><Sidebar as="span"><A /><B /></Sidebar></AnyCheckbox>
97
107
  ```
@@ -2,14 +2,17 @@ const MULTI_CHILDREN_REGEX = /(Cluster|Stack)$/
2
2
  const REGEX_NLSP = /^\s*\n+\s*$/
3
3
  const FLEX_END_REGEX = /^(flex-)?end$/
4
4
 
5
- const TARGET_INVALID_COMPONENT_REGEX = /(Center|Cluster|Container|Reel|Sidebar)$/
6
- const INVALID_ELEMENT = `JSXOpeningElement[name.name=${TARGET_INVALID_COMPONENT_REGEX}]`
5
+ const LAYOUT_COMPONENT_REGEX_WITHOUT_STACK = /(Center|Cluster|Container|Reel|Sidebar)$/
6
+ const LAYOUT_COMPONENT_REGEX = /(Center|Cluster|Container|Reel|Stack|Sidebar)$/
7
+ const LAYOUT_COMPONENT_ELEMENT_WITHOUT_STACK = `JSXOpeningElement[name.name=${LAYOUT_COMPONENT_REGEX_WITHOUT_STACK}]`
7
8
  const HEADING_ELEMENT = 'JSXElement[openingElement.name.name=/Heading$/]'
8
- const STACK_ELEMENT_NOT_SPAN = 'JSXOpeningElement[name.name=/Stack$/]:not(:has(JSXAttribute[name.name=/^(as|forwardedAs)$/][value.value="span"]))'
9
+ const NOT_HAS_AS_SPAN_ATTRIBUTE = ':not(:has(JSXAttribute[name.name=/^(as|forwardedAs)$/][value.value="span"]))'
10
+ const STACK_ELEMENT_NOT_SPAN = `JSXOpeningElement[name.name=/Stack$/]${NOT_HAS_AS_SPAN_ATTRIBUTE}`
11
+ const LAYOUT_ELEMENT_NOT_SPAN = `JSXOpeningElement[name.name=${LAYOUT_COMPONENT_REGEX}]${NOT_HAS_AS_SPAN_ATTRIBUTE}`
9
12
  const FORM_CONTROL_LABEL_ATTRIBUTE = 'JSXOpeningElement[name.name=/FormControl$/] JSXAttribute[name.name="label"]'
10
13
  const FIELDSET_LEGEND_ATTRIBUTE = 'JSXOpeningElement[name.name=/Fieldset$/] JSXAttribute[name.name="legend"]'
11
14
  const ICON_ELEMENT_WITH_TEXT = `JSXOpeningElement[name.name=/Icon$/]:has(JSXAttribute[name.name="text"])`
12
- const TEXT_ELEMENT_WITH_PREFIX = 'JSXOpeningElement[name.name=/Text$/]:has(JSXAttribute[name.name="prefixIcon"])'
15
+ const TEXT_ELEMENT_WITH_PREFIX = 'JSXOpeningElement[name.name=/Text$/]:has(JSXAttribute[name.name=/^(prefixIcon|icon)$/])'
13
16
 
14
17
  const filterFalsyJSXText = (cs) => cs.filter(checkFalsyJSXText)
15
18
  const checkFalsyJSXText = (c) => (
@@ -51,6 +54,9 @@ const searchChildren = (node) => {
51
54
  return true
52
55
  }
53
56
 
57
+ const DETAIL_LINK_MESSAGE = `
58
+ - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts`
59
+
54
60
  const SCHEMA = []
55
61
 
56
62
  /**
@@ -91,8 +97,7 @@ module.exports = {
91
97
  } else if (gapAttr?.value.type === 'JSXExpressionContainer' && gapAttr.value.expression.value === 0) {
92
98
  context.report({
93
99
  node,
94
- message: `${nodeName} に "gap={0}" が指定されており、smarthr-ui/${layoutType} の利用方法として誤っている可能性があります。以下の修正方法を検討してください。
95
- - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts
100
+ message: `${nodeName} に "gap={0}" が指定されており、smarthr-ui/${layoutType} の利用方法として誤っている可能性があります。以下の修正方法を検討してください。${DETAIL_LINK_MESSAGE}
96
101
  - 方法1: 子要素を一つにまとめられないか検討してください
97
102
  - 例: "<Stack gap={0}><p>hoge</p><p>fuga</p></Stack>" を "<p>hoge<br />fuga</p>" にするなど
98
103
  - 方法2: 子要素のstyleを確認しgap属性を0以外にできないか検討してください
@@ -116,79 +121,84 @@ module.exports = {
116
121
  node,
117
122
  message:
118
123
  (justifyAttr?.value.value === 'center' || alignAttr?.value.value === 'center')
119
- ? `${nodeName} は smarthr-ui/${layoutType} ではなく smarthr-ui/Center でマークアップしてください
120
- - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts`
121
- : `${nodeName}には子要素が一つしか無いため、${layoutType}でマークアップする意味がありません。
122
- - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts
124
+ ? `${nodeName} は smarthr-ui/${layoutType} ではなく smarthr-ui/Center でマークアップしてください${DETAIL_LINK_MESSAGE}`
125
+ : `${nodeName}には子要素が一つしか無いため、${layoutType}でマークアップする意味がありません。${DETAIL_LINK_MESSAGE}
123
126
  - styleを確認し、div・spanなど、別要素でマークアップし直すか、${nodeName}を削除してください
124
127
  - as, forwardedAsなどでSectioningContent系要素に変更している場合、対応するsmarthr-ui/Section, Aside, Nav, Article のいずれかに差し替えてください`
125
128
  })
126
129
  }
127
130
  }
128
131
  },
129
- [`${HEADING_ELEMENT} ${INVALID_ELEMENT}`]: (node) => {
130
- const component = node.name.name.match(TARGET_INVALID_COMPONENT_REGEX)[1]
132
+ [`${HEADING_ELEMENT} ${LAYOUT_COMPONENT_ELEMENT_WITHOUT_STACK}`]: (node) => {
133
+ const component = node.name.name.match(LAYOUT_COMPONENT_REGEX_WITHOUT_STACK)[1]
131
134
 
132
135
  context.report({
133
136
  node,
134
- message: `Headingの子孫に${component}を置くことはできません。Headingの外で${component}を使用するようにマークアップを修正してください。
135
- - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts`
137
+ message: `Headingの子孫に${component}を置くことはできません。Headingの外で${component}を使用するようにマークアップを修正してください。${DETAIL_LINK_MESSAGE}`
136
138
  })
137
139
  },
138
140
  [`${HEADING_ELEMENT} ${STACK_ELEMENT_NOT_SPAN}`]: (node) => {
139
141
  context.report({
140
142
  node,
141
- message: `Headingの子孫にStackを置く場合、as属性、もしくはforwardedAs属性に \`span\` を指定してください
142
- - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts`,
143
+ message: `Headingの子孫にStackを置く場合、as属性、もしくはforwardedAs属性に \`span\` を指定してください${DETAIL_LINK_MESSAGE}`,
143
144
  })
144
145
  },
145
146
  [`${HEADING_ELEMENT} :matches(${ICON_ELEMENT_WITH_TEXT},${TEXT_ELEMENT_WITH_PREFIX})`]: (node) => {
146
147
  context.report({
147
148
  node,
148
- message: `HeadingにIconを設定する場合 <Heading icon={<XxxIcon />}></Heading> のようにicon属性を利用してください
149
- - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts`,
149
+ message: `HeadingにIconを設定する場合 <Heading icon={<XxxIcon />}></Heading> のようにicon属性を利用してください${DETAIL_LINK_MESSAGE}`,
150
150
  })
151
151
  },
152
- [`${FORM_CONTROL_LABEL_ATTRIBUTE} ${INVALID_ELEMENT}`]: (node) => {
152
+ [`${FORM_CONTROL_LABEL_ATTRIBUTE} ${LAYOUT_COMPONENT_ELEMENT_WITHOUT_STACK}`]: (node) => {
153
153
  context.report({
154
154
  node,
155
- message: `FormControlのlabel属性に${node.name.name.match(TARGET_INVALID_COMPONENT_REGEX)[1]}を置くことはできません。ラベル用テキスト以外をstatusLabels、subActionArea、もしくはlabel属性のObjectとして '{ text: テキスト, icon: <XxxIcon /> }'に置き換えてください。
156
- - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts`,
155
+ message: `FormControlのlabel属性に${node.name.name.match(LAYOUT_COMPONENT_REGEX_WITHOUT_STACK)[1]}を置くことはできません。ラベル用テキスト以外をstatusLabels、subActionArea、もしくはlabel属性のObjectとして '{ text: テキスト, icon: <XxxIcon /> }'に置き換えてください。${DETAIL_LINK_MESSAGE}`,
157
156
  })
158
157
  },
159
158
  [`${FORM_CONTROL_LABEL_ATTRIBUTE} ${STACK_ELEMENT_NOT_SPAN}`]: (node) => {
160
159
  context.report({
161
160
  node,
162
- message: `FormControlのlabel属性にStackを置く場合、as属性、もしくはforwardedAs属性に \`span\` を指定してください
163
- - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts`,
161
+ message: `FormControlのlabel属性にStackを置く場合、as属性、もしくはforwardedAs属性に \`span\` を指定してください${DETAIL_LINK_MESSAGE}`,
164
162
  })
165
163
  },
166
164
  [`${FORM_CONTROL_LABEL_ATTRIBUTE} :matches(${ICON_ELEMENT_WITH_TEXT},${TEXT_ELEMENT_WITH_PREFIX})`]: (node) => {
167
165
  context.report({
168
166
  node,
169
- message: `FormControlのlabel属性にアイコンを設定する場合 <FormControl label={{ text: 'テキスト', icon: <XxxIcon /> }} /> のようにlabel.icon属性を利用してください
170
- - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts`,
167
+ message: `FormControlのlabel属性にアイコンを設定する場合 <FormControl label={{ text: 'テキスト', icon: <XxxIcon /> }} /> のようにlabel.icon属性を利用してください${DETAIL_LINK_MESSAGE}`,
171
168
  })
172
169
  },
173
- [`${FIELDSET_LEGEND_ATTRIBUTE} ${INVALID_ELEMENT}`]: (node) => {
170
+ [`${FIELDSET_LEGEND_ATTRIBUTE} ${LAYOUT_COMPONENT_ELEMENT_WITHOUT_STACK}`]: (node) => {
174
171
  context.report({
175
172
  node,
176
- message: `Fieldsetのlegend属性に${node.name.name.match(TARGET_INVALID_COMPONENT_REGEX)[1]}を置くことはできません。ラベル用テキスト以外をstatusLabels、subActionArea、もしくはlabel属性のObjectとして '{ text: テキスト, icon: <XxxIcon /> }'に置き換えてください。
177
- - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts`,
173
+ message: `Fieldsetのlegend属性に${node.name.name.match(LAYOUT_COMPONENT_REGEX_WITHOUT_STACK)[1]}を置くことはできません。ラベル用テキスト以外をstatusLabels、subActionArea、もしくはlabel属性のObjectとして '{ text: テキスト, icon: <XxxIcon /> }'に置き換えてください。${DETAIL_LINK_MESSAGE}`,
178
174
  })
179
175
  },
180
176
  [`${FIELDSET_LEGEND_ATTRIBUTE} ${STACK_ELEMENT_NOT_SPAN}`]: (node) => {
181
177
  context.report({
182
178
  node,
183
- message: `Fieldsetのlegend属性にStackを置く場合、as属性、もしくはforwardedAs属性に \`span\` を指定してください
184
- - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts`,
179
+ message: `Fieldsetのlegend属性にStackを置く場合、as属性、もしくはforwardedAs属性に \`span\` を指定してください${DETAIL_LINK_MESSAGE}`,
185
180
  })
186
181
  },
187
182
  [`${FIELDSET_LEGEND_ATTRIBUTE} :matches(${ICON_ELEMENT_WITH_TEXT},${TEXT_ELEMENT_WITH_PREFIX})`]: (node) => {
188
183
  context.report({
189
184
  node,
190
- message: `Fieldsetのlegend属性にアイコンを設定する場合 <Fieldset legend={{ text: 'テキスト', icon: <XxxIcon /> }} /> のようにlegend.icon属性を利用してください
191
- - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts`,
185
+ message: `Fieldsetのlegend属性にアイコンを設定する場合 <Fieldset legend={{ text: 'テキスト', icon: <XxxIcon /> }} /> のようにlegend.icon属性を利用してください${DETAIL_LINK_MESSAGE}`,
186
+ })
187
+ },
188
+ [`JSXElement:has(>JSXOpeningElement[name.name=/RadioButton(Panel)?$/]) ${LAYOUT_ELEMENT_NOT_SPAN}`]: (node) => {
189
+ const component = node.name.name.match(LAYOUT_COMPONENT_REGEX)[1]
190
+
191
+ context.report({
192
+ node,
193
+ message: `RadioButton, RadioButtonPanelの子孫に${component}を置く場合、as属性、もしくはforwardedAs属性に \`span\` を指定してください${DETAIL_LINK_MESSAGE}`,
194
+ })
195
+ },
196
+ [`JSXElement:has(>JSXOpeningElement[name.name=/Checkbox?$/]) ${LAYOUT_ELEMENT_NOT_SPAN}`]: (node) => {
197
+ const component = node.name.name.match(LAYOUT_COMPONENT_REGEX)[1]
198
+
199
+ context.report({
200
+ node,
201
+ message: `Checkboxの子孫に${component}を置く場合、as属性、もしくはforwardedAs属性に \`span\` を指定してください${DETAIL_LINK_MESSAGE}`,
192
202
  })
193
203
  },
194
204
  }
@@ -0,0 +1,46 @@
1
+ # smarthr/best-practice-for-prohibit-import-smarthr-ui-local
2
+
3
+ - smarthr-uiの内部的に利用している型などのimportを禁止します
4
+ - 型は必要とするコンポーネントなどから生成することをおすすめします
5
+
6
+ ## rules
7
+
8
+ ```js
9
+ {
10
+ rules: {
11
+ 'smarthr/best-practice-for-prohibit-import-smarthr-ui-local': 'error', // 'warn', 'off'
12
+ },
13
+ }
14
+ ```
15
+
16
+ ## ❌ Incorrect
17
+
18
+ ```jsx
19
+ // smarthr-uiの内部構造に依存したpathからimportしているためNG
20
+ import { AnchorButton } from 'smarthr-ui/lib/components/Button/AnchorButton'
21
+ import { FaArrowRightIcon } from 'smarthr-ui/lib/components/Icon'
22
+
23
+ // 型情報は内部実装、storybookのためにexportしているもののため利用するとNG
24
+ import { HeadingTagTypes } from 'smarthr-ui/lib/components/Heading/Heading'
25
+ import type { Variant } from 'smarthr-ui/lib/components/Button/types'
26
+ import { type Navigation } from 'smarthr-ui/lib/components/AppHeader/types'
27
+ ```
28
+
29
+ ## ✅ Correct
30
+
31
+ ```jsx
32
+ // 'smarthr-ui' からimportしているのでOK
33
+ import { AnchorButton, FaArrowRightIcon } from 'smarthr-ui'
34
+
35
+ // 型情報をコンポーネントから生成しているのでOK
36
+ import { ComponentProps } from 'react'
37
+
38
+ import { Heading } from 'smarthr-ui'
39
+ type HeadingTagTypes = Required<ComponentProps<typeof Heading>>['tag']
40
+
41
+ import { Button } from 'smarthr-ui'
42
+ type Variant = Required<ComponentProps<typeof Button>>['variant']
43
+
44
+ import { AppHeader } from 'smarthr-ui'
45
+ type Navigation = Exclude<ComponentProps<typeof AppHeader>['navigations'], undefined | null>[number]
46
+ ```
@@ -0,0 +1,25 @@
1
+ const SCHEMA = []
2
+
3
+ /**
4
+ * @type {import('@typescript-eslint/utils').TSESLint.RuleModule<''>}
5
+ */
6
+ module.exports = {
7
+ meta: {
8
+ type: 'problem',
9
+ schema: SCHEMA,
10
+ },
11
+ create(context) {
12
+ return {
13
+ [`ImportDeclaration[source.value=/^smarthr-ui\\u002Flib\\u002Fcomponents\\u002F/]`]: (node) => {
14
+ context.report({
15
+ node,
16
+ message: `smarthr-uiからコンポーネントや型をimportする際は 'smarthr-ui' からimportしてください
17
+ - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-prohibit-import-smarthr-ui-local
18
+ - 'smarthr-ui/lib/components' 以下からのexportはsmarthr-uiの内部実装・もしくはstorybook用であり、プロダクトからの利用は非推奨です
19
+ - 型を使いたい場合、コンポーネントからreact/ComponentPropsを利用し生成するように修正してください`,
20
+ })
21
+ },
22
+ }
23
+ },
24
+ }
25
+ module.exports.schema = SCHEMA
@@ -10,8 +10,10 @@ const ruleTester = new RuleTester({
10
10
  },
11
11
  },
12
12
  })
13
- const errorMessage = (type, name) => `${name}には子要素が一つしか無いため、${type}でマークアップする意味がありません。
14
- - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts
13
+
14
+ const DETAIL_LINK_MESSAGE = `
15
+ - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts`
16
+ const errorMessage = (type, name) => `${name}には子要素が一つしか無いため、${type}でマークアップする意味がありません。${DETAIL_LINK_MESSAGE}
15
17
  - styleを確認し、div・spanなど、別要素でマークアップし直すか、${name}を削除してください
16
18
  - as, forwardedAsなどでSectioningContent系要素に変更している場合、対応するsmarthr-ui/Section, Aside, Nav, Article のいずれかに差し替えてください`
17
19
 
@@ -65,6 +67,9 @@ ruleTester.run('best-practice-for-button-element', rule, {
65
67
  { code: `<AnyFormControl label={{ text: 'hoge', icon: <AnyIcon /> }} />` },
66
68
  { code: `<AnyFieldset legend={{ text: <AnyStack as="span"><span>a</span><span>b</span></AnyStack> }} />` },
67
69
  { code: `<Fieldset legend={{ text: 'hoge', icon: <AnyIcon /> }} />` },
70
+ { code: `<AnyRadioButton><Cluster as="span"><A /><B /></Cluster></AnyRadioButton>` },
71
+ { code: `<RadioButtonPanel><AnyStack forwardedAs="span"><A /><B /></AnyStack></RadioButtonPanel>` },
72
+ { code: `<AnyCheckbox><Sidebar as="span"><A /><B /></Sidebar></AnyCheckbox>` },
68
73
  ],
69
74
  invalid: [
70
75
  { code: `<Stack><Hoge /></Stack>`, errors: [ { message: errorMessage('Stack', 'Stack') } ] },
@@ -89,8 +94,7 @@ ruleTester.run('best-practice-for-button-element', rule, {
89
94
  { code: `<AnyCluster>{a ? a.b.hoge(action) : <Hoge />}</AnyCluster>`, errors: [ { message: errorMessage('Cluster', 'AnyCluster') } ] },
90
95
  { code: `<AnyCluster>{a ? <Hoge /> : a.b.hoge(action)}</AnyCluster>`, errors: [ { message: errorMessage('Cluster', 'AnyCluster') } ] },
91
96
  { code: `<AnyCluster>{a ? <Hoge /> : a ? <Hoge /> : a.b.hoge(action)}</AnyCluster>`, errors: [ { message: errorMessage('Cluster', 'AnyCluster') } ] },
92
- { code: `<HogeStack gap={0}>{a}{b}</HogeStack>`, errors: [ { message: `HogeStack に "gap={0}" が指定されており、smarthr-ui/Stack の利用方法として誤っている可能性があります。以下の修正方法を検討してください。
93
- - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts
97
+ { code: `<HogeStack gap={0}>{a}{b}</HogeStack>`, errors: [ { message: `HogeStack に "gap={0}" が指定されており、smarthr-ui/Stack の利用方法として誤っている可能性があります。以下の修正方法を検討してください。${DETAIL_LINK_MESSAGE}
94
98
  - 方法1: 子要素を一つにまとめられないか検討してください
95
99
  - 例: "<Stack gap={0}><p>hoge</p><p>fuga</p></Stack>" を "<p>hoge<br />fuga</p>" にするなど
96
100
  - 方法2: 子要素のstyleを確認しgap属性を0以外にできないか検討してください
@@ -98,30 +102,21 @@ ruleTester.run('best-practice-for-button-element', rule, {
98
102
  - 方法3: 別要素でマークアップし直すか、HogeStackを削除してください
99
103
  - 親要素に smarthr-ui/Cluster, smarthr-ui/Stack などが存在している場合、div・spanなどで1要素にまとめる必要がある場合があります
100
104
  - as, forwardedAsなどでSectioningContent系要素に変更している場合、対応するsmarthr-ui/Section, Aside, Nav, Article のいずれかに差し替えてください` } ] },
101
- { code: `<Heading><Cluster><Hoge /><Fuga /></Cluster></Heading>`, errors: [ { message: `Headingの子孫にClusterを置くことはできません。Headingの外でClusterを使用するようにマークアップを修正してください。
102
- - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts` } ] },
103
- { code: `<AnyHeading><AnyStack><Hoge /><Fuga /></AnyStack></AnyHeading>`, errors: [ { message: `Headingの子孫にStackを置く場合、as属性、もしくはforwardedAs属性に \`span\` を指定してください
104
- - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts` } ] },
105
- { code: `<Heading><AnyIcon text="hoge" /></Heading>`, errors: [ { message: `HeadingにIconを設定する場合 <Heading icon={<XxxIcon />}></Heading> のようにicon属性を利用してください
106
- - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts` } ] },
107
- { code: `<AnyHeading><Text prefixIcon={<SomeIcon />}>hoge</Text></AnyHeading>`, errors: [ { message: `HeadingにIconを設定する場合 <Heading icon={<XxxIcon />}></Heading> のようにicon属性を利用してください
108
- - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts` } ] },
109
- { code: `<AnyFormControl label={{ text: <Cluster><Hoge /><Fuga /></Cluster> }} />`, errors: [ { message: `FormControllabel属性にClusterを置くことはできません。ラベル用テキスト以外をstatusLabels、subActionArea、もしくはlabel属性のObjectとして '{ text: テキスト, icon: <XxxIcon /> }'に置き換えてください。
110
- - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts` } ] },
111
- { code: `<FormControl label={<AnyStack><Hoge /><Fuga /></AnyStack>} />`, errors: [ { message: `FormControllabel属性にStackを置く場合、as属性、もしくはforwardedAs属性に \`span\` を指定してください
112
- - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts` } ] },
113
- { code: `<AnyFormControl label={{ text: <AnyIcon text="hoge" /> }} />`, errors: [ { message: `FormControlのlabel属性にアイコンを設定する場合 <FormControl label={{ text: 'テキスト', icon: <XxxIcon /> }} /> のようにlabel.icon属性を利用してください
114
- - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts` } ] },
115
- { code: `<FormControl label={{ text: <Text prefixIcon={<SomeIcon /> }>hoge</Text>}} />`, errors: [ { message: `FormControlのlabel属性にアイコンを設定する場合 <FormControl label={{ text: 'テキスト', icon: <XxxIcon /> }} /> のようにlabel.icon属性を利用してください
116
- - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts` } ] },
117
- { code: `<Fieldset legend={<Cluster><Hoge /><Fuga /></Cluster>} />`, errors: [ { message: `Fieldsetのlegend属性にClusterを置くことはできません。ラベル用テキスト以外をstatusLabels、subActionArea、もしくはlabel属性のObjectとして '{ text: テキスト, icon: <XxxIcon /> }'に置き換えてください。
118
- - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts` } ] },
119
- { code: `<AnyFieldset legend={{ text: <AnyStack><Hoge /><Fuga /></AnyStack> }} />`, errors: [ { message: `Fieldsetのlegend属性にStackを置く場合、as属性、もしくはforwardedAs属性に \`span\` を指定してください
120
- - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts` } ] },
121
- { code: `<Fieldset legend={<AnyIcon text="hoge" />} />`, errors: [ { message: `Fieldsetのlegend属性にアイコンを設定する場合 <Fieldset legend={{ text: 'テキスト', icon: <XxxIcon /> }} /> のようにlegend.icon属性を利用してください
122
- - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts` } ] },
123
- { code: `<AnyFieldset legend={{ text: <Text prefixIcon={<SomeIcon />}>hoge</Text>}} />`, errors: [ { message: `Fieldsetのlegend属性にアイコンを設定する場合 <Fieldset legend={{ text: 'テキスト', icon: <XxxIcon /> }} /> のようにlegend.icon属性を利用してください
124
- - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts` } ] },
105
+ { code: `<Heading><Cluster><Hoge /><Fuga /></Cluster></Heading>`, errors: [ { message: `Headingの子孫にClusterを置くことはできません。Headingの外でClusterを使用するようにマークアップを修正してください。${DETAIL_LINK_MESSAGE}` } ] },
106
+ { code: `<AnyHeading><AnyStack><Hoge /><Fuga /></AnyStack></AnyHeading>`, errors: [ { message: `Headingの子孫にStackを置く場合、as属性、もしくはforwardedAs属性に \`span\` を指定してください${DETAIL_LINK_MESSAGE}` } ] },
107
+ { code: `<Heading><AnyIcon text="hoge" /></Heading>`, errors: [ { message: `HeadingにIconを設定する場合 <Heading icon={<XxxIcon />}></Heading> のようにicon属性を利用してください${DETAIL_LINK_MESSAGE}` } ] },
108
+ { code: `<AnyHeading><Text prefixIcon={<SomeIcon />}>hoge</Text></AnyHeading>`, errors: [ { message: `HeadingにIconを設定する場合 <Heading icon={<XxxIcon />}></Heading> のようにicon属性を利用してください${DETAIL_LINK_MESSAGE}` } ] },
109
+ { code: `<AnyFormControl label={{ text: <Cluster><Hoge /><Fuga /></Cluster> }} />`, errors: [ { message: `FormControlのlabel属性にClusterを置くことはできません。ラベル用テキスト以外をstatusLabels、subActionArea、もしくはlabel属性のObjectとして '{ text: テキスト, icon: <XxxIcon /> }'に置き換えてください。${DETAIL_LINK_MESSAGE}` } ] },
110
+ { code: `<FormControl label={<AnyStack><Hoge /><Fuga /></AnyStack>} />`, errors: [ { message: `FormControlのlabel属性にStackを置く場合、as属性、もしくはforwardedAs属性に \`span\` を指定してください${DETAIL_LINK_MESSAGE}` } ] },
111
+ { code: `<AnyFormControl label={{ text: <AnyIcon text="hoge" /> }} />`, errors: [ { message: `FormControlのlabel属性にアイコンを設定する場合 <FormControl label={{ text: 'テキスト', icon: <XxxIcon /> }} /> のようにlabel.icon属性を利用してください${DETAIL_LINK_MESSAGE}` } ] },
112
+ { code: `<FormControl label={{ text: <Text prefixIcon={<SomeIcon /> }>hoge</Text>}} />`, errors: [ { message: `FormControlのlabel属性にアイコンを設定する場合 <FormControl label={{ text: 'テキスト', icon: <XxxIcon /> }} /> のようにlabel.icon属性を利用してください${DETAIL_LINK_MESSAGE}` } ] },
113
+ { code: `<Fieldset legend={<Cluster><Hoge /><Fuga /></Cluster>} />`, errors: [ { message: `Fieldsetlegend属性にClusterを置くことはできません。ラベル用テキスト以外をstatusLabels、subActionArea、もしくはlabel属性のObjectとして '{ text: テキスト, icon: <XxxIcon /> }'に置き換えてください。${DETAIL_LINK_MESSAGE}` } ] },
114
+ { code: `<AnyFieldset legend={{ text: <AnyStack><Hoge /><Fuga /></AnyStack> }} />`, errors: [ { message: `Fieldsetのlegend属性にStackを置く場合、as属性、もしくはforwardedAs属性に \`span\` を指定してください${DETAIL_LINK_MESSAGE}` } ] },
115
+ { code: `<Fieldset legend={<AnyIcon text="hoge" />} />`, errors: [ { message: `Fieldsetlegend属性にアイコンを設定する場合 <Fieldset legend={{ text: 'テキスト', icon: <XxxIcon /> }} /> のようにlegend.icon属性を利用してください${DETAIL_LINK_MESSAGE}` } ] },
116
+ { code: `<AnyFieldset legend={{ text: <Text prefixIcon={<SomeIcon />}>hoge</Text>}} />`, errors: [ { message: `Fieldsetのlegend属性にアイコンを設定する場合 <Fieldset legend={{ text: 'テキスト', icon: <XxxIcon /> }} /> のようにlegend.icon属性を利用してください${DETAIL_LINK_MESSAGE}` } ] },
117
+ { code: `<AnyRadioButton><Cluster><A /><B /></Cluster></AnyRadioButton>`, errors: [ { message: `RadioButton, RadioButtonPanelの子孫にClusterを置く場合、as属性、もしくはforwardedAs属性に \`span\` を指定してください${DETAIL_LINK_MESSAGE}` } ] },
118
+ { code: `<RadioButtonPanel><AnyStack><A /><B /></AnyStack></RadioButtonPanel>`, errors: [ { message: `RadioButton, RadioButtonPanelの子孫にStackを置く場合、as属性、もしくはforwardedAs属性に \`span\` を指定してください${DETAIL_LINK_MESSAGE}` } ] },
119
+ { code: `<AnyCheckbox><Sidebar><A /><B /></Sidebar></AnyCheckbox>`, errors: [ { message: `Checkboxの子孫にSidebarを置く場合、as属性、もしくはforwardedAs属性に \`span\` を指定してください${DETAIL_LINK_MESSAGE}` } ] },
125
120
  ]
126
121
  })
127
122
 
@@ -0,0 +1,27 @@
1
+ const rule = require('../rules/best-practice-for-prohibit-import-smarthr-ui-local')
2
+ const RuleTester = require('eslint').RuleTester
3
+
4
+ const ERROR_MESSAGE = `smarthr-uiからコンポーネントや型をimportする際は 'smarthr-ui' からimportしてください
5
+ - 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-prohibit-import-smarthr-ui-local
6
+ - 'smarthr-ui/lib/components' 以下からのexportはsmarthr-uiの内部実装・もしくはstorybook用であり、プロダクトからの利用は非推奨です
7
+ - 型を使いたい場合、コンポーネントからreact/ComponentPropsを利用し生成するように修正してください`
8
+
9
+ const ruleTester = new RuleTester({
10
+ languageOptions: {
11
+ parserOptions: {
12
+ ecmaFeatures: {
13
+ jsx: true,
14
+ },
15
+ },
16
+ },
17
+ })
18
+ ruleTester.run('best-practice-for-prohibit-import-smarthr-ui-local', rule, {
19
+ valid: [
20
+ { code: `import { AnchorButton } from 'smarthr-ui'` },
21
+ ],
22
+ invalid: [
23
+ { code: `import { AnchorButton } from 'smarthr-ui/lib/components/Button/AnchorButton'`, errors: [ { message: ERROR_MESSAGE } ] },
24
+ { code: `import { FaArrowRightIcon } from 'smarthr-ui/lib/components/Icon'`, errors: [ { message: ERROR_MESSAGE } ] },
25
+ { code: `import { HeadingTagTypes } from 'smarthr-ui/lib/components/Heading/Heading'`, errors: [ { message: ERROR_MESSAGE } ] },
26
+ ]
27
+ })