eslint-plugin-smarthr
smarthr/a11y-icon-button-has-name
- ボタンの中にIconのみが設置されている場合、アクセシビリティの問題が発生する可能性を防ぐルールです
rules
{
rules: {
'smarthr/a11y-icon-button-has-name': 'error', // 'warn', 'off'
},
}
❌ Incorrect
<XxxButton>
<YyyIcon />
</XxxButton>
✅ Correct
<XxxButton>
<YyyIcon />
Hoge
</XxxButton>
<XxxButton>
<YyyIcon />
<AnyComponent />
</XxxButton>
<XxxButton>
<YyyIcon visuallyHiddenText="hoge" />
</XxxButton>
smarthr/format-import-path
- importする際のpathをフォーマットするruleです
- ディレクトリ構造からドメインを識別して相対パス、絶対パスいずれかにするかを判定することが出来ます
- 例: crews/index 以下同士でのimportは相対パス、crews/index外のファイルimportする場合は絶対パスにする
- eslint を
--fix
オプション付きで実行すると自動的にパスを置き換えます
config
- tsconfig.json の compilerOptions.pathsに '@/*' としてroot path を指定する必要があります
- ドメインを識別するために以下の設定を記述する必要があります
- globalModuleDir
- 全体で利用するファイルを収めているディレクトリを相対パスで指定します
- domainModuleDir:
- ドメイン内で共通のファイルを収めているディレクトリ名を指定します
- domainConstituteDir
ディレクトリ例
/ constants
/ modules // 全体共通ディレクトリ
/ crews
/ modules // 共通ディレクトリ
/ views
/ parts
/ index
/ adapters
/ index.ts
/ hoge.ts
/ slices
/ index.ts
/ views
/ index.ts
/ parts
/ Abc.ts
/ show
/ views
/ parts
指定例
const DOMAIN_RULE_ARGS = {
globalModuleDir: [ './constants', './modules' ],
domainModuleDir: [ 'modules' ],
domainConstituteDir: [ 'adapters', 'slices', 'views', 'parts' ],
}
rules
{
rules: {
'smarthr/format-import-path': [
'error', // 'warn', 'off'
{
...DOMAIN_RULE_ARGS,
format: {
// 'relative', 'auto', 'none'
// all: 'absolute',
outside: 'auto',
globalModule: 'absolute',
module: 'relative',
domain: 'relative',
lower: 'relative',
},
},
],
},
}
❌ Incorrect
// crews/index/views/index.js
import slice from '@/crews/index/slice'
import hoge from '@/crews/index/adapter/hoge'
import Abc from '@/crews/index/views/parts/Abc'
import modulePart from '@/crews/modules/views/part'
import showPart from '../../show/views/parts'
import globalModulePart from '../../../module/views/part'
✅ Correct
// crews/index/views/index.js
import slice from '../slice'
import hoge from '../adapter/hoge'
import Abc from './parts/Abc'
import modulePart from '../../modules/views/parts'
import showPart from '@/crews/show/views/parts'
import globalModulePart from '@/modules/views/parts'
smarthr/jsx-start-with-spread-attributes
- jsxを記述する際、意図しない属性の上書きを防ぐため、spread-attributesを先に指定するように強制するruleです
rules
{
rules: {
'smarthr/jsx-start-with-spread-attributes': 'error', // 'warn', 'off'
},
}
❌ Incorrect
<AnyComponent hoge="hoge" {...props} />
✅ Correct
<AnyComponent {...props} hoge="hoge" />
smarthr/no-import-other-domain
- ドメイン外からのimportを防ぐruleです
- 例: crews/index 以下からのimportはOK, crews/index から crews/show 以下をimportするとNG
- ディレクトリ構造からドメインを識別して判定することが出来ます
config
- tsconfig.json の compilerOptions.pathsに '@/*' としてroot path を指定する必要があります
- ドメインを識別するために以下の設定を記述する必要があります
- globalModuleDir
- 全体で利用するファイルを収めているディレクトリを相対パスで指定します
- domainModuleDir:
- ドメイン内で共通のファイルを収めているディレクトリ名を指定します
- domainConstituteDir
ディレクトリ例
/ constants
/ modules // 全体共通ディレクトリ
/ crews
/ modules // 共通ディレクトリ
/ views
/ parts
/ index
/ adapters
/ index.ts
/ hoge.ts
/ slices
/ index.ts
/ views
/ index.ts
/ parts
/ Abc.ts
/ show
/ views
/ parts
指定例
const DOMAIN_RULE_ARGS = {
globalModuleDir: [ './constants', './modules' ],
domainModuleDir: [ 'modules' ],
domainConstituteDir: [ 'adapters', 'slices', 'views', 'parts' ],
}
rules
{
rules: {
'smarthr/no-import-other-domain': [
'error', // 'warn', 'off'
{
...DOMAIN_RULE_ARGS,
// analyticsMode: 'all', // 'same-domain', 'another-domain'
}
]
},
}
❌ Incorrect
// crews/index/views/index.js
import showPart1 from '@/crews/show/views/parts'
import showPart2 from '../../show/views/parts'
✅ Correct
// crews/index/views/index.js
import slice from '../slice'
import hoge from '../adapter/hoge'
import Abc from './parts/Abc'
import modulePart from '../../modules/views/parts'
import globalModulePart from '@/modules/views/parts'
smarthr/prohibit-import
- 例えば特定の module にバグが発見されたなど、importさせたくない場合に利用するルールです
rules
{
rules: {
'smarthr/prohibit-import': [
'error', // 'warn', 'off'
{
targets: {
'^query-string$': true, // key は 正規表現を指定する
'^smarthr-ui$': ['SecondaryButtonAnchor'],
},
// generateReportMessage: (source, imported) =>
// `${source}${imported && `/${imported}`} はXxxxxxなので利用せず yyyy/zzzz を利用してください`
}
]
},
}
❌ Incorrect
import queryString from 'query-string'
import { SecondaryButtonAnchor } from 'smarthr-ui'
✅ Correct
import { PrimaryButton, SecondaryButton } from 'smarthr-ui'
smarthr/redundant-name
- ファイル、コードの冗長な部分を取り除くことを提案するruleです
- ファイルが設置されているディレクトリ構造からキーワードを生成し、取り除く文字列を生成します
config
- tsconfig.json の compilerOptions.pathsに '@/*' としてroot path を指定する必要があります
- 以下の設定を記述する必要があります
- ignoreKeywords
- ディレクトリ名から生成されるキーワードに含めたくない文字列を指定します
- keywordGenerator
- ディレクトリ名から生成されるキーワードを元に最終的なキーワードを生成します
- suffixGenerator:
- type のみ指定出来ます
- type のsuffixを生成します
指定例
const ignorekeywords = ['views', 'parts']
const keywordGenerator = ({ keywords }) => (
keywords.reduce((prev, keyword, index) => {
switch (keyword) {
case 'repositories':
return [...prev, keyword, 'repository']
}
const replacedKeyword = keyword.replace(/(repository|s)$/, '')
if (keyword !== replacedKeyword) {
return [...prev, keyword, replacedKeyword]
}
return [...prev, keyword]
}, [])
)
// 例: actions 以下の場合だけ 'Action' もしくは `Actions` のSuffixを許可する
const suffixGenerator = ({ node, filename }) => {
let suffix = ['Props', 'Type']
if (filename.match(/\/actions\//)) {
suffix = [
isUnionType || (node.typeAnnotation.type === 'TSTypeReference' && node.id.name.match(/Actions$/))
? 'Actions'
: 'Action',
...suffix,
]
}
}
ファイル例
@/crews/index/views/page.tsx
の場合
- 生成されるキーワードは
['crews', 'crew', 'index', 'page']
@/crews/index/views/parts/Abc.tsx
の場合
- 生成されるキーワードは
['crews', 'crew', 'index', 'Abc']
@/crews/index/repositories/index.ts
の場合
- 生成されるキーワードは
['crews', 'crew', 'index', 'repositories', 'repository']
rules
{
rules: {
'smarthr/redundant-name': [
'error', // 'warn', 'off'
{
type: { ignorekeywords, keywordGenerator, suffixGenerator },
file: { ignorekeywords, keywordGenerator },
// property: { ignorekeywords, keywordGenerator },
// function: { ignorekeywords, keywordGenerator },
// variable: { ignorekeywords, keywordGenerator },
// class: { ignorekeywords, keywordGenerator },
}
]
},
}
❌ Incorrect
// @/crews/index/views/page.tsx
type CrewIndexPage = { hoge: string }
type CrewsView = { hoge: string }
// @/crews/show/repositories/index.tsx
type CrewIndexRepository = { hoge: () => any }
✅ Correct
// @/crews/index/views/page.tsx
type ItemProps = { hoge: string }
// @/crews/show/repositories/index.tsx
type IndexProps = { hoge: () => any }
type ResponseType = { hoge: () => any }
``