eslint-plugin-smarthr 0.2.3 → 0.2.4

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,13 @@
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.2.4](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.2.3...v0.2.4) (2022-08-30)
6
+
7
+
8
+ ### Features
9
+
10
+ * ruleを更新する ([#27](https://github.com/kufu/eslint-plugin-smarthr/issues/27)) ([1ec7783](https://github.com/kufu/eslint-plugin-smarthr/commit/1ec7783395c9dafa547c453724f3671df489997b))
11
+
5
12
  ### [0.2.3](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.2.2...v0.2.3) (2022-08-26)
6
13
 
7
14
 
package/libs/common.js CHANGED
@@ -28,7 +28,7 @@ const rootPath = (() => {
28
28
  return ''
29
29
  }
30
30
 
31
- const p = (replacePaths['@/'] || [])[0]
31
+ const p = (replacePaths['@/'] || replacePaths['~/'] || [])[0]
32
32
 
33
33
  if (!p) {
34
34
  return ''
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-smarthr",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "author": "SmartHR",
5
5
  "license": "MIT",
6
6
  "description": "A sharable ESLint plugin for SmartHR",
@@ -1,6 +1,7 @@
1
1
  const { generateTagFormatter } = require('../../libs/format_styled_components')
2
2
 
3
3
  const EXPECTED_NAMES = {
4
+ 'SmartHRLogo$': 'SmartHRLogo$',
4
5
  '(b|B)utton$': 'Button$',
5
6
  'Anchor$': 'Anchor$',
6
7
  'Link$': 'Link$',
@@ -35,23 +36,17 @@ module.exports = {
35
36
  return
36
37
  }
37
38
 
38
- const recursiveSearch = (c) => {
39
- if (['JSXText', 'JSXExpressionContainer'].includes(c.type)) {
40
- return true
41
- }
42
-
43
- if (c.type === 'JSXElement') {
44
- if (c.openingElement.attributes.some((a) => (['visuallyHiddenText', 'alt'].includes(a.name.name) && !!a.value.value))) {
45
- return true
46
- }
47
-
48
- if (c.children && filterFalsyJSXText(c.children).some(recursiveSearch)) {
49
- return true
50
- }
51
- }
52
-
53
- return false
54
- }
39
+ const recursiveSearch = (c) => (
40
+ ['JSXText', 'JSXExpressionContainer'].includes(c.type) ||
41
+ (
42
+ c.type === 'JSXElement' && (
43
+ // HINT: SmartHRLogo コンポーネントは内部でaltを持っているため対象外にする
44
+ c.openingElement.name.name.match(/SmartHRLogo$/) ||
45
+ c.openingElement.attributes.some((a) => (['visuallyHiddenText', 'alt'].includes(a.name.name) && !!a.value.value)) ||
46
+ (c.children && filterFalsyJSXText(c.children).some(recursiveSearch))
47
+ )
48
+ )
49
+ )
55
50
 
56
51
  const child = filterFalsyJSXText(parentNode.children).find(recursiveSearch)
57
52
 
@@ -47,7 +47,21 @@ module.exports = {
47
47
  return
48
48
  }
49
49
 
50
- filterFalsyJSXText(parentNode.children).forEach((c) => {
50
+ const children = filterFalsyJSXText(parentNode.children)
51
+
52
+ if (children.length > 1) {
53
+ context.report({
54
+ node,
55
+ messageId: 'a11y-trigger-has-button',
56
+ data: {
57
+ message: `${match[1]}Trigger の直下には複数のコンポーネントを設置することは出来ません。buttonコンポーネントが一つだけ設置されている状態にしてください`,
58
+ },
59
+ })
60
+
61
+ return
62
+ }
63
+
64
+ children.forEach((c) => {
51
65
  // `<DialogTrigger>{button}</DialogTrigger>` のような場合は許可する
52
66
  if (c.type === 'JSXExpressionContainer') {
53
67
  return false
@@ -56,7 +70,7 @@ module.exports = {
56
70
  if (
57
71
  c.type !== 'JSXElement' ||
58
72
  !c.openingElement.name.name.match(/(b|B)utton$/) ||
59
- c.openingElement.name.name.match(/AnchorButton?/)
73
+ c.openingElement.name.name.match(/AnchorButton$/)
60
74
  ) {
61
75
  context.report({
62
76
  node: c,
@@ -16,7 +16,6 @@ const DEFAULT_CONFIG = {
16
16
  ],
17
17
  SUFFIX: ['Props', 'Type'],
18
18
  },
19
- typeProperty: COMMON_DEFAULT_CONFIG,
20
19
  file: COMMON_DEFAULT_CONFIG,
21
20
  property: COMMON_DEFAULT_CONFIG,
22
21
  function: COMMON_DEFAULT_CONFIG,
@@ -62,7 +61,6 @@ const SCHEMA = [
62
61
  ...DEFAULT_SCHEMA_PROPERTY,
63
62
  suffix: { type: 'array', items: { type: 'string' } },
64
63
  },
65
- typeProperty: DEFAULT_SCHEMA_PROPERTY,
66
64
  file: DEFAULT_SCHEMA_PROPERTY,
67
65
  property: DEFAULT_SCHEMA_PROPERTY,
68
66
  function: DEFAULT_SCHEMA_PROPERTY,
@@ -89,7 +87,6 @@ const generateRedundantKeywords = ({ args, key, terminalImportName }) => {
89
87
  const option = args.option[key] || {}
90
88
  const ignoreKeywords = option.ignoreKeywords || DEFAULT_CONFIG[key].IGNORE_KEYWORDS
91
89
  const terminalImportKeyword = terminalImportName ? terminalImportName.toLowerCase() : ''
92
-
93
90
  return args.keywords.reduce((prev, keyword) => {
94
91
  if (keyword === terminalImportKeyword || ignoreKeywords.includes(keyword)) {
95
92
  return prev
@@ -262,8 +259,7 @@ const generateTypeRedundant = (args) => {
262
259
  }
263
260
 
264
261
  const generateTypePropertyRedundant = (args) => {
265
- const key = 'typeProperty'
266
-
262
+ const key = 'property'
267
263
  return handleReportBetterName({
268
264
  ...args,
269
265
  key,
@@ -274,7 +270,7 @@ const generateTypePropertyRedundant = (args) => {
274
270
  })
275
271
  }
276
272
  const generateTypePropertyFunctionParamsRedundant = (args) => {
277
- const key = 'typeProperty'
273
+ const key = 'property'
278
274
  const redundant = handleReportBetterName({
279
275
  ...args,
280
276
  key,
@@ -298,7 +294,15 @@ const generatePropertyRedundant = (args) => {
298
294
  option: args.option[key],
299
295
  redundantKeywords: generateRedundantKeywords({ args, key }),
300
296
  defaultBetterName: 'item',
301
- fetchName: (node) => node.key.name,
297
+ fetchName: (node) => {
298
+ // argumentsとしてわたされたobjectの展開などの場合は許可する
299
+ // このファイル内で修正すべき場合などは冗長な名前を修正するべき場合はtype propertyなどで判断出来る
300
+ if (node.parent.type === 'ObjectPattern') {
301
+ return null
302
+ }
303
+
304
+ return node.key.name
305
+ },
302
306
  })
303
307
  }
304
308
 
@@ -391,7 +395,6 @@ module.exports = {
391
395
  'file-name': ' {{ message }}',
392
396
  'type-name': '{{ message }}',
393
397
  'type-name/invalid-suffix': '{{ message }}',
394
- 'typeProperty-name': '{{ message }}',
395
398
  'property-name': ' {{ message }}',
396
399
  'function-name': ' {{ message }}',
397
400
  'functionParams-name': ' {{ message }}',
@@ -449,9 +452,10 @@ module.exports = {
449
452
  addRule('TSTypeAliasDeclaration', generateTypeRedundant(args))
450
453
  // addRule('TSInterfaceDeclaration', generateTypeRedundant(args)) // 必要になったら実装する
451
454
  }
452
- if (option.typeProperty) {
455
+ if (option.property) {
453
456
  const typePropRedundant = generateTypePropertyRedundant(args)
454
457
  const typeFuncParamRedundant = generateTypePropertyFunctionParamsRedundant(args)
458
+ const redundant = generatePropertyRedundant(args)
455
459
 
456
460
  addRule('TSPropertySignature', (node) => {
457
461
  typePropRedundant(node)
@@ -460,10 +464,6 @@ module.exports = {
460
464
  typeFuncParamRedundant(node.typeAnnotation.typeAnnotation)
461
465
  }
462
466
  })
463
- }
464
- if (option.property) {
465
- const redundant = generatePropertyRedundant(args)
466
-
467
467
  addRule('Property', redundant)
468
468
  addRule('PropertyDefinition', redundant)
469
469
  }
@@ -104,7 +104,7 @@ module.exports = {
104
104
  sourceValue = sources.join('/')
105
105
  }
106
106
 
107
- if (barrel) {
107
+ if (barrel && !barrel.match(new RegExp(`^${rootPath}/index\.`))) {
108
108
  barrel = calculateReplacedImportPath(barrel)
109
109
 
110
110
  context.report({
@@ -26,6 +26,7 @@ ruleTester.run('a11y-clickable-element-has-text', rule, {
26
26
  { code: 'const HogeLink = styled(Link)``' },
27
27
  { code: 'const HogeButton = styled(Button)``' },
28
28
  { code: 'const FugaAnchor = styled(HogeAnchor)``' },
29
+ { code: 'const FugaSmartHRLogo = styled(SmartHRLogo)``' },
29
30
  {
30
31
  code: `<a>ほげ</a>`,
31
32
  },
@@ -89,6 +90,12 @@ ruleTester.run('a11y-clickable-element-has-text', rule, {
89
90
  {
90
91
  code: `<a><span>{any}</span></a>`,
91
92
  },
93
+ {
94
+ code: `<a><SmartHRLogo /></a>`,
95
+ },
96
+ {
97
+ code: `<a><PrefixSmartHRLogo /></a>`,
98
+ },
92
99
  ],
93
100
  invalid: [
94
101
  { code: `import hoge from 'styled-components'`, errors: [ { message: "styled-components をimportする際は、名称が`styled` となるようにしてください。例: `import styled from 'styled-components'`" } ] },
@@ -98,6 +105,8 @@ ruleTester.run('a11y-clickable-element-has-text', rule, {
98
105
  { code: 'const Hoge = styled(Link)``', errors: [ { message: `Hogeを正規表現 "/Link$/" がmatchする名称に変更してください` } ] },
99
106
  { code: 'const Hoge = styled(Button)``', errors: [ { message: `Hogeを正規表現 "/Button$/" がmatchする名称に変更してください` } ] },
100
107
  { code: 'const Fuga = styled(HogeAnchor)``', errors: [ { message: `Fugaを正規表現 "/Anchor$/" がmatchする名称に変更してください` } ] },
108
+ { code: 'const Fuga = styled(HogeAnchor)``', errors: [ { message: `Fugaを正規表現 "/Anchor$/" がmatchする名称に変更してください` } ] },
109
+ { code: 'const Fuga = styled(SmartHRLogo)``', errors: [ { message: `Fugaを正規表現 "/SmartHRLogo$/" がmatchする名称に変更してください` } ] },
101
110
  {
102
111
  code: `<a><img src="hoge.jpg" /></a>`,
103
112
  errors: [{ message: defaultErrorMessage }]
@@ -138,5 +147,9 @@ ruleTester.run('a11y-clickable-element-has-text', rule, {
138
147
  code: `<button><AnyComponent><Icon visuallyHiddenText="" /></AnyComponent></button>`,
139
148
  errors: [{ message: defaultErrorMessage }]
140
149
  },
150
+ {
151
+ code: `<button><SmartHRLogoSuffix /></button>`,
152
+ errors: [{ message: defaultErrorMessage }]
153
+ },
141
154
  ]
142
155
  })
@@ -33,18 +33,20 @@ ruleTester.run('a11y-trigger-has-button', rule, {
33
33
  ],
34
34
  invalid: [
35
35
  { code: `import hoge from 'styled-components'`, errors: [ { message: "styled-components をimportする際は、名称が`styled` となるようにしてください。例: `import styled from 'styled-components'`" } ] },
36
- { code: 'const Hoge = styled.button``', errors: [ { message: `Hogeを正規表現 "/Button$/" がmatchする名称に変更してください` } ] },
37
- { code: 'const Hoge = styled.a``', errors: [ { message: `Hogeを正規表現 "/(Anchor|Link)$/" がmatchする名称に変更してください` } ] },
38
- { code: 'const Hoge = styled(Button)``', errors: [ { message: `Hogeを正規表現 "/Button$/" がmatchする名称に変更してください` } ] },
39
- { code: 'const Hoge = styled(AnchorButton)``', errors: [ { message: `Hogeを正規表現 "/Button$/" がmatchする名称に変更してください` },{ message: `Hogeを正規表現 "/AnchorButton$/" がmatchする名称に変更してください` } ] },
40
- { code: 'const Hoge = styled(ButtonAnchor)``', errors: [ { message: `Hogeを正規表現 "/ButtonAnchor$/" がmatchする名称に変更してください` }, { message: `Hogeを正規表現 "/Anchor$/" がmatchする名称に変更してください` } ] },
41
- { code: 'const Hoge = styled(Anchor)``', errors: [ { message: `Hogeを正規表現 "/Anchor$/" がmatchする名称に変更してください` } ] },
42
- { code: 'const Hoge = styled(Link)``', errors: [ { message: `Hogeを正規表現 "/Link$/" がmatchする名称に変更してください` } ] },
43
- { code: 'const Hoge = styled(DropdownTrigger)``', errors: [ { message: `Hogeを正規表現 "/DropdownTrigger$/" がmatchする名称に変更してください` } ] },
44
- { code: 'const Hoge = styled(DialogTrigger)``', errors: [ { message: `Hogeを正規表現 "/DialogTrigger$/" がmatchする名称に変更してください` } ] },
45
- { code: '<DropdownTrigger>ほげ</DropdownTrigger>', errors: [ { message: 'DropdownTrigger の直下にはbuttonコンポーネントのみ設置してください' } ] },
46
- { code: '<DialogTrigger><span><Button>ほげ</Button></span></DialogTrigger>', errors: [ { message: 'DialogTrigger の直下にはbuttonコンポーネントのみ設置してください' } ] },
47
- { code: '<DropdownTrigger><AnchorButton>ほげ</AnchorButton></DropdownTrigger>', errors: [ { message: 'DropdownTrigger の直下にはbuttonコンポーネントのみ設置してください' } ] },
48
- { code: '<DropdownTrigger><ButtonAnchor>ほげ</ButtonAnchor></DropdownTrigger>', errors: [ { message: 'DropdownTrigger の直下にはbuttonコンポーネントのみ設置してください' } ] },
36
+ { code: 'const Hoge = styled.button``', errors: [ { message: `Hogeを正規表現 "/Button$/" がmatchする名称に変更してください` } ] },
37
+ { code: 'const Hoge = styled.a``', errors: [ { message: `Hogeを正規表現 "/(Anchor|Link)$/" がmatchする名称に変更してください` } ] },
38
+ { code: 'const Hoge = styled(Button)``', errors: [ { message: `Hogeを正規表現 "/Button$/" がmatchする名称に変更してください` } ] },
39
+ { code: 'const Hoge = styled(AnchorButton)``', errors: [ { message: `Hogeを正規表現 "/Button$/" がmatchする名称に変更してください` },{ message: `Hogeを正規表現 "/AnchorButton$/" がmatchする名称に変更してください` } ] },
40
+ { code: 'const Hoge = styled(ButtonAnchor)``', errors: [ { message: `Hogeを正規表現 "/ButtonAnchor$/" がmatchする名称に変更してください` }, { message: `Hogeを正規表現 "/Anchor$/" がmatchする名称に変更してください` } ] },
41
+ { code: 'const Hoge = styled(Anchor)``', errors: [ { message: `Hogeを正規表現 "/Anchor$/" がmatchする名称に変更してください` } ] },
42
+ { code: 'const Hoge = styled(Link)``', errors: [ { message: `Hogeを正規表現 "/Link$/" がmatchする名称に変更してください` } ] },
43
+ { code: 'const Hoge = styled(DropdownTrigger)``', errors: [ { message: `Hogeを正規表現 "/DropdownTrigger$/" がmatchする名称に変更してください` } ] },
44
+ { code: 'const Hoge = styled(DialogTrigger)``', errors: [ { message: `Hogeを正規表現 "/DialogTrigger$/" がmatchする名称に変更してください` } ] },
45
+ { code: '<DropdownTrigger>ほげ</DropdownTrigger>', errors: [ { message: 'DropdownTrigger の直下にはbuttonコンポーネントのみ設置してください' } ] },
46
+ { code: '<DialogTrigger><span><Button>ほげ</Button></span></DialogTrigger>', errors: [ { message: 'DialogTrigger の直下にはbuttonコンポーネントのみ設置してください' } ] },
47
+ { code: '<DropdownTrigger><AnchorButton>ほげ</AnchorButton></DropdownTrigger>', errors: [ { message: 'DropdownTrigger の直下にはbuttonコンポーネントのみ設置してください' } ] },
48
+ { code: '<DropdownTrigger><ButtonAnchor>ほげ</ButtonAnchor></DropdownTrigger>', errors: [ { message: 'DropdownTrigger の直下にはbuttonコンポーネントのみ設置してください' } ] },
49
+ { code: '<DialogTrigger><button>{hoge}</button>{hoge}</DialogTrigger>', errors: [ { message: 'DialogTrigger の直下には複数のコンポーネントを設置することは出来ません。buttonコンポーネントが一つだけ設置されている状態にしてください' } ] },
50
+ { code: '<DropdownTrigger>{hoge}<span>text</span></DropdownTrigger>', errors: [ { message: 'DropdownTrigger の直下には複数のコンポーネントを設置することは出来ません。buttonコンポーネントが一つだけ設置されている状態にしてください' } ] },
49
51
  ]
50
52
  })