Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

eslint-plugin-smarthr

Package Overview
Dependencies
Maintainers
22
Versions
84
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

eslint-plugin-smarthr - npm Package Compare versions

Comparing version 0.3.3 to 0.3.4

7

CHANGELOG.md

@@ -5,2 +5,9 @@ # Changelog

### [0.3.4](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.3.3...v0.3.4) (2023-07-18)
### Features
* a11y-heading-in-sectioning-content を smarthr-ui/PageHeading に対応させる ([#66](https://github.com/kufu/eslint-plugin-smarthr/issues/66)) ([5abc13c](https://github.com/kufu/eslint-plugin-smarthr/commit/5abc13c73f57242fbe8b6016bc6e598fd0403f1c))
### [0.3.3](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.3.2...v0.3.3) (2023-07-10)

@@ -7,0 +14,0 @@

2

package.json
{
"name": "eslint-plugin-smarthr",
"version": "0.3.3",
"version": "0.3.4",
"author": "SmartHR",

@@ -5,0 +5,0 @@ "license": "MIT",

const { generateTagFormatter } = require('../../libs/format_styled_components')
const EXPECTED_NAMES = {
'PageHeading$': 'PageHeading$',
'Heading$': 'Heading$',
'^h(1|2|3|4|5|6)$': 'Heading$',
'^h1$': 'PageHeading$',
'^h(|2|3|4|5|6)$': 'Heading$',
'Article$': 'Article$',

@@ -10,2 +12,3 @@ 'Aside$': 'Aside$',

'Section$': 'Section$',
'ModelessDialog$': 'ModelessDialog$',
}

@@ -17,8 +20,13 @@

const bareTagRegex = /^(article|aside|nav|section)$/
const messagePrefix = 'Headingと紐づく内容の範囲(アウトライン)が曖昧になっています。'
const messageSuffix = 'Sectioning Content(Article, Aside, Nav, Section)でHeadingコンポーネントと内容をラップしてHeadingに対応する範囲を明確に指定してください。現在のマークアップの構造を変更したくない場合はSectioningFragmentコンポーネントを利用してください。'
const modelessDialogRegex = /ModelessDialog$/
const commonMessage = `${messagePrefix}${messageSuffix}`
const rootMessage = `${messagePrefix}コンポーネント全体に対するHeadingではない場合、${messageSuffix}コンポーネント全体に対するHeadingの場合、他のHeadingのアウトラインが明確に指定されればエラーにならなくなります。`
const noHeadingTagNames = ['span', 'legend']
const headingMessage = `smarthr-ui/Headingと紐づく内容の範囲(アウトライン)が曖昧になっています。
- smarthr-uiのArticle, Aside, Nav, SectionのいずれかでHeadingコンポーネントと内容をラップしてHeadingに対応する範囲を明確に指定してください。`
const rootHeadingMessage = `${headingMessage}
- Headingをh1にしたい場合(機能名、ページ名などこのページ内でもっとも重要な見出しの場合)、smarthr-ui/PageHeadingを利用してください。その場合はSectionなどでアウトラインを示す必要はありません。`
const pageHeadingMessage = 'smarthr-ui/PageHeading が同一ファイル内に複数存在しています。PageHeadingはh1タグを出力するため最も重要な見出しにのみ利用してください。'
const pageHeadingInSectionMessage = 'smarthr-ui/PageHeadingはsmarthr-uiのArticle, Aside, Nav, Sectionで囲まないでください。囲んでしまうとページ全体の見出しではなくなってしまいます。'
const reportMessageBareToSHR = (tagName, visibleExample) => {

@@ -43,4 +51,8 @@ const matcher = tagName.match(bareTagRegex)

// Headingコンポーネントの拡張なので対象外
if (node.type === 'VariableDeclarator' && node.id.name.match(declaratorHeadingRegex)) {
if (
// Headingコンポーネントの拡張なので対象外
node.type === 'VariableDeclarator' && node.parent.parent?.type === 'Program' && node.id.name.match(declaratorHeadingRegex) ||
// ModelessDialogのheaderにHeadingを設定している場合も対象外
node.type === 'JSXAttribute' && node.name.name === 'header' && node.parent.name.name.match(modelessDialogRegex)
) {
return null

@@ -58,2 +70,3 @@ }

create(context) {
let h1s = []
let sections = []

@@ -93,27 +106,38 @@ let { VariableDeclarator, ...formatter } = generateTagFormatter({ context, EXPECTED_NAMES })

})
} else if (elementName.match(headingRegex)) {
// Headingに明示的にtag属性が設定されており、それらが span or legend の場合はHeading扱いしない
} else if (
elementName.match(headingRegex) &&
!noHeadingTagNames.includes(node.attributes.find((a) => a.name?.name == 'tag')?.value.value)
) {
const result = searchBubbleUp(node.parent)
if (result) {
const saved = sections.find((s) => s[0] === result)
if (elementName.match(/PageHeading$/)) {
h1s.push(node)
// HINT: 最初の1つ目は通知しない()
if (!saved) {
sections.push([result, node])
if (h1s.length > 1) {
context.report({
node,
message: pageHeadingMessage,
})
} else if (result.type !== 'Program') {
context.report({
node,
message: pageHeadingInSectionMessage,
})
}
} else if (result.type === 'Program') {
context.report({
node,
message: rootHeadingMessage,
})
} else {
// HINT: 同じファイルで同じSectioningContent or トップノードを持つ場合
const [section, unreport] = saved
const targets = unreport ? [unreport, node] : [node]
saved[1] = undefined
targets.forEach((n) => {
if (sections.find((s) => s === result)) {
context.report({
node: n,
message:
section.type === 'Program'
? rootMessage
: commonMessage,
node,
message: headingMessage,
})
})
} else {
sections.push(result)
}
}

@@ -120,0 +144,0 @@ }

@@ -42,10 +42,15 @@ # smarthr/a11y-heading-in-sectioning-content

## ✅ Correct
```jsx
<div>
<Heading>hoge</Heading> // コンポーネント内にHeadingが一つのみの場合はOK
</div>
<>
<PageHeading> // 同じファイル内に複数のPageHeadingが存在するとNG
hoge
</PageHeading>
<PageHeading>
fuga
</PageHeading>
</>
```
## ✅ Correct
```jsx

@@ -61,12 +66,15 @@ <Section>

```jsx
<Section>
<Heading>
hoge
</Heading>
</Section>
<StyledSection>
<Heading>
fuga
</Heading>
</StyledSection>
<>
<PageHeading>Page Name.</PageHeading>
<Section>
<Heading>
hoge
</Heading>
</Section>
<StyledSection>
<Heading>
fuga
</Heading>
</StyledSection>
</>
```

@@ -15,4 +15,8 @@ const rule = require('../rules/a11y-heading-in-sectioning-content');

const rootMessage = 'Headingと紐づく内容の範囲(アウトライン)が曖昧になっています。コンポーネント全体に対するHeadingではない場合、Sectioning Content(Article, Aside, Nav, Section)でHeadingコンポーネントと内容をラップしてHeadingに対応する範囲を明確に指定してください。現在のマークアップの構造を変更したくない場合はSectioningFragmentコンポーネントを利用してください。コンポーネント全体に対するHeadingの場合、他のHeadingのアウトラインが明確に指定されればエラーにならなくなります。'
const message = 'Headingと紐づく内容の範囲(アウトライン)が曖昧になっています。Sectioning Content(Article, Aside, Nav, Section)でHeadingコンポーネントと内容をラップしてHeadingに対応する範囲を明確に指定してください。現在のマークアップの構造を変更したくない場合はSectioningFragmentコンポーネントを利用してください。'
const lowerMessage = `smarthr-ui/Headingと紐づく内容の範囲(アウトライン)が曖昧になっています。
- smarthr-uiのArticle, Aside, Nav, SectionのいずれかでHeadingコンポーネントと内容をラップしてHeadingに対応する範囲を明確に指定してください。`
const message = `${lowerMessage}
- Headingをh1にしたい場合(機能名、ページ名などこのページ内でもっとも重要な見出しの場合)、smarthr-ui/PageHeadingを利用してください。その場合はSectionなどでアウトラインを示す必要はありません。`
const pageMessage = 'smarthr-ui/PageHeading が同一ファイル内に複数存在しています。PageHeadingはh1タグを出力するため最も重要な見出しにのみ利用してください。'
const pageInSectionMessage = 'smarthr-ui/PageHeadingはsmarthr-uiのArticle, Aside, Nav, Sectionで囲まないでください。囲んでしまうとページ全体の見出しではなくなってしまいます。'

@@ -22,3 +26,3 @@ ruleTester.run('a11y-heading-in-sectioning-content', rule, {

{ code: `import styled from 'styled-components'` },
{ code: 'const HogeHeading = styled.h1``' },
{ code: 'const HogePageHeading = styled.h1``' },
{ code: 'const HogeHeading = styled.h2``' },

@@ -35,14 +39,10 @@ { code: 'const HogeHeading = styled.h3``' },

{ code: 'const FugaSection = styled(HogeSection)``' },
{ code: '<Heading>hoge</Heading>' },
{ code: '<HogeHeading>hoge</HogeHeading>' },
{ code: '<><Heading>hoge</Heading><Section><Heading>hoge</Heading></Section></>' },
{ code: '<><Heading>hoge</Heading><Aside><Heading>hoge</Heading></Aside></>' },
{ code: '<><Heading>hoge</Heading><Article><Heading>hoge</Heading></Article></>' },
{ code: '<><Heading>hoge</Heading><Nav><Heading>hoge</Heading></Nav></>' },
{ code: '<><Heading>hoge</Heading><SectioningFragment><Heading>hoge</Heading></SectioningFragment></>' },
{ code: 'const HogeHeading = () => <FugaHeading anyArg={abc}>hoge</FugaHeading>;const FugaHeading = () => <AbcHeading anyArg={abc}>hoge</AbcHeading>' },
{ code: '<PageHeading>hoge</PageHeading>' },
{ code: '<Section><Heading>hoge</Heading></Section>' },
{ code: '<><Section><Heading>hoge</Heading></Section><Section><Heading>fuga</Heading></Section></>' },
{ code: 'const HogeHeading = () => <FugaHeading anyArg={abc}>hoge</FugaHeading>' },
],
invalid: [
{ code: `import hoge from 'styled-components'`, errors: [ { message: `styled-components をimportする際は、名称が"styled" となるようにしてください。例: "import styled from 'styled-components'"` } ] },
{ code: 'const Hoge = styled.h1``', errors: [ { message: `Hogeを正規表現 "/Heading$/" がmatchする名称に変更してください` } ] },
{ code: 'const Hoge = styled.h1``', errors: [ { message: `Hogeを正規表現 "/PageHeading$/" がmatchする名称に変更してください` } ] },
{ code: 'const Hoge = styled.h2``', errors: [ { message: `Hogeを正規表現 "/Heading$/" がmatchする名称に変更してください` } ] },

@@ -63,10 +63,9 @@ { code: 'const Hoge = styled.h3``', errors: [ { message: `Hogeを正規表現 "/Heading$/" がmatchする名称に変更してください` } ] },

{ code: 'const StyledSection = styled.section``', errors: [ { message: `"section"を利用せず、smarthr-ui/Sectionを拡張してください。Headingのレベルが自動計算されるようになります。(例: "styled.section" -> "styled(Section)")` } ] },
{ code: '<><Heading>hoge</Heading><Heading>hoge</Heading></>', errors: [ { message: rootMessage }, { message: rootMessage } ] },
{ code: '<><Heading>hoge</Heading><Section><Heading>hoge</Heading><Heading>hoge</Heading></Section></>', errors: [ { message }, { message } ] },
{ code: '<article>hoge</article>', errors: [ { message: `"article"を利用せず、smarthr-ui/Articleを拡張してください。Headingのレベルが自動計算されるようになります。` } ] },
{ code: '<aside>hoge</aside>', errors: [ { message: `"aside"を利用せず、smarthr-ui/Asideを拡張してください。Headingのレベルが自動計算されるようになります。` } ] },
{ code: '<nav>hoge</nav>', errors: [ { message: `"nav"を利用せず、smarthr-ui/Navを拡張してください。Headingのレベルが自動計算されるようになります。` } ] },
{ code: '<section>hoge</section>', errors: [ { message: `"section"を利用せず、smarthr-ui/Sectionを拡張してください。Headingのレベルが自動計算されるようになります。` } ] },
{ code: 'const Hoge = () => <FugaHeading anyArg={abc}>hoge</FugaHeading>;const Fuga = () => <AbcHeading anyArg={abc}>hoge</AbcHeading>', errors: [ { message: rootMessage }, { message: rootMessage } ] },
{ code: '<><PageHeading>hoge</PageHeading><PageHeading>fuga</PageHeading></>', errors: [ { message: pageMessage } ] },
{ code: '<Heading>hoge</Heading>', errors: [ { message } ] },
{ code: '<><Heading>hoge</Heading><Heading>fuga</Heading></>', errors: [ { message }, { message } ] },
{ code: 'const Hoge = () => <FugaHeading anyArg={abc}>hoge</FugaHeading>', errors: [ { message } ] },
{ code: '<Section><Heading>hoge</Heading><Heading>fuga</Heading></Section>', errors: [ { message: lowerMessage } ] },
{ code: '<Section><PageHeading>hoge</PageHeading></Section>', errors: [ { message: pageInSectionMessage } ] },
],
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc