diff --git a/libs/format_styled_components.js b/libs/format_styled_components.js index 0ed68fe..b546724 100644 --- a/libs/format_styled_components.js +++ b/libs/format_styled_components.js @@ -1,68 +1,71 @@ -const getExtendedComponentName = (node) => { - if (!node.parent) { - return null - } +const STYLED_COMPONENTS_METHOD = 'styled' +const STYLED_COMPONENTS = `${STYLED_COMPONENTS_METHOD}-components` - return node.parent.id?.name || getExtendedComponentName(node.parent) -} -const getBaseComponentName = (node) => { - if (!node) { - return null - } +const findInvalidImportNameNode = (s) => s.type === 'ImportDefaultSpecifier' && s.local.name !== STYLED_COMPONENTS_METHOD - if (node.type === 'CallExpression') { - if (node.callee.name === 'styled') { - return node.arguments[0].name - } - if (node.callee.object?.name === 'styled') { - return node.callee.property.name - } - } +const generateTagFormatter = ({ context, EXPECTED_NAMES }) => { + const entriesesTagNames = Object.entries(EXPECTED_NAMES).map(([b, e]) => [ new RegExp(b), new RegExp(e) ]) - if (node?.object?.name === 'styled') { - return node.property.name - } + return { + ImportDeclaration: (node) => { + if (node.source.value !== STYLED_COMPONENTS) { + return + } - return getBaseComponentName(node.parent) -} + const invalidNameNode = node.specifiers.find(findInvalidImportNameNode) -const generateTagFormatter = ({ context, EXPECTED_NAMES }) => ({ - ImportDeclaration: (node) => { - if (node.source.value !== 'styled-components') { - return - } + if (invalidNameNode) { + context.report({ + node: invalidNameNode, + message: `${STYLED_COMPONENTS} をimportする際は、名称が"${STYLED_COMPONENTS_METHOD}" となるようにしてください。例: "import ${STYLED_COMPONENTS_METHOD} from '${STYLED_COMPONENTS}'"`, + }); + } + }, + VariableDeclarator: (node) => { + if (!node.init) { + return + } - const invalidNameNode = node.specifiers.find((s) => s.type === 'ImportDefaultSpecifier' && s.local.name !== 'styled') + const tag = node.init.tag || node.init - if (invalidNameNode) { - context.report({ - node: invalidNameNode, - message: "styled-components をimportする際は、名称が`styled` となるようにしてください。例: `import styled from 'styled-components'`", - }); - } - }, - TaggedTemplateExpression: (node) => { - const extended = getExtendedComponentName(node) + let base = null - if (extended) { - const base = getBaseComponentName(node.tag) + if (tag.object?.name === STYLED_COMPONENTS_METHOD) { + base = tag.property.name + } else if (tag.callee) { + const callee = tag.callee + + switch (STYLED_COMPONENTS_METHOD) { + case callee.name: { + const arg = tag.arguments[0] + base = arg.name || arg.value + break + } + case callee.callee?.name: { + const arg = callee.arguments[0] + base = arg.name || arg.value + break + } + case callee.object?.name: + base = callee.property.name + break + } + } if (base) { - Object.entries(EXPECTED_NAMES).forEach(([b, e]) => { - if (base.match(new RegExp(b))) { - const extendedregex = new RegExp(e) + const extended = node.id.name - if (!extended.match(extendedregex)) { - context.report({ - node: node.parent, - message: `${extended}を正規表現 "${extendedregex.toString()}" がmatchする名称に変更してください`, - }); - } + entriesesTagNames.forEach(([b, e]) => { + if (base.match(b) && !extended.match(e)) { + context.report({ + node, + message: `${extended}を正規表現 "${e.toString()}" がmatchする名称に変更してください`, + }); } }) } - } - }, -}) + }, + } +} module.exports = { generateTagFormatter } diff --git a/test/a11y-clickable-element-has-text.js b/test/a11y-clickable-element-has-text.js index e440f1e..1e3dd77 100644 --- a/test/a11y-clickable-element-has-text.js +++ b/test/a11y-clickable-element-has-text.js @@ -27,6 +27,9 @@ ruleTester.run('a11y-clickable-element-has-text', rule, { { code: 'const HogeButton = styled(Button)``' }, { code: 'const FugaAnchor = styled(HogeAnchor)``' }, { code: 'const FugaSmartHRLogo = styled(SmartHRLogo)``' }, + { code: 'const HogeAnchor = styled.a(() => ``)' }, + { code: 'const HogeAnchor = styled("a")(() => ``)' }, + { code: 'const HogeAnchor = styled(Anchor)(() => ``)' }, { code: `ほげ`, }, @@ -110,7 +113,7 @@ ruleTester.run('a11y-clickable-element-has-text', rule, { }, ], invalid: [ - { code: `import hoge from 'styled-components'`, errors: [ { message: "styled-components をimportする際は、名称が`styled` となるようにしてください。例: `import styled from 'styled-components'`" } ] }, + { code: `import hoge from 'styled-components'`, errors: [ { message: `styled-components をimportする際は、名称が"styled" となるようにしてください。例: "import styled from 'styled-components'"` } ] }, { code: 'const Hoge = styled.a``', errors: [ { message: `Hogeを正規表現 "/(Anchor|Link)$/" がmatchする名称に変更してください` } ] }, { code: 'const Hoge = styled.button``', errors: [ { message: `Hogeを正規表現 "/Button$/" がmatchする名称に変更してください` } ] }, { code: 'const Hoge = styled(Anchor)``', errors: [ { message: `Hogeを正規表現 "/Anchor$/" がmatchする名称に変更してください` } ] }, @@ -119,6 +122,10 @@ ruleTester.run('a11y-clickable-element-has-text', rule, { { code: 'const Fuga = styled(HogeAnchor)``', errors: [ { message: `Fugaを正規表現 "/Anchor$/" がmatchする名称に変更してください` } ] }, { code: 'const Fuga = styled(HogeAnchor)``', errors: [ { message: `Fugaを正規表現 "/Anchor$/" がmatchする名称に変更してください` } ] }, { code: 'const Fuga = styled(SmartHRLogo)``', errors: [ { message: `Fugaを正規表現 "/SmartHRLogo$/" がmatchする名称に変更してください` } ] }, + { code: 'const Piyo = styled.a(() => ``)', errors: [ { message: `Piyoを正規表現 "/(Anchor|Link)$/" がmatchする名称に変更してください` } ] }, + { code: 'const Piyo = styled("a")(() => ``)', errors: [ { message: `Piyoを正規表現 "/(Anchor|Link)$/" がmatchする名称に変更してください` } ] }, + { code: 'const Piyo = styled("a")``', errors: [ { message: `Piyoを正規表現 "/(Anchor|Link)$/" がmatchする名称に変更してください` } ] }, + { code: 'const Piyo = styled(Anchor)(() => ``)', errors: [ { message: `Piyoを正規表現 "/Anchor$/" がmatchする名称に変更してください` } ] }, { code: ``, errors: [{ message: defaultErrorMessage }] diff --git a/test/a11y-image-has-alt-attribute.js b/test/a11y-image-has-alt-attribute.js index 05cea08..9d526e6 100644 --- a/test/a11y-image-has-alt-attribute.js +++ b/test/a11y-image-has-alt-attribute.js @@ -33,7 +33,7 @@ ruleTester.run('a11y-image-has-alt-attribute', rule, { { code: '' }, ], invalid: [ - { code: `import hoge from 'styled-components'`, errors: [ { message: "styled-components をimportする際は、名称が`styled` となるようにしてください。例: `import styled from 'styled-components'`" } ] }, + { code: `import hoge from 'styled-components'`, errors: [ { message: `styled-components をimportする際は、名称が"styled" となるようにしてください。例: "import styled from 'styled-components'"` } ] }, { code: 'const Hoge = styled.img``', errors: [ { message: `Hogeを正規表現 "/(Img|Image|Icon)$/" がmatchする名称に変更してください` } ] }, { code: 'const Hoge = styled.svg``', errors: [ { message: `Hogeを正規表現 "/(Img|Image|Icon)$/" がmatchする名称に変更してください` } ] }, { code: 'const Hoge = styled(Icon)``', errors: [ { message: `Hogeを正規表現 "/Icon$/" がmatchする名称に変更してください` } ] }, diff --git a/test/a11y-input-has-name-attribute.js b/test/a11y-input-has-name-attribute.js index a754618..2e2f393 100644 --- a/test/a11y-input-has-name-attribute.js +++ b/test/a11y-input-has-name-attribute.js @@ -33,7 +33,7 @@ ruleTester.run('a11y-input-has-name-attribute', rule, { { code: '