Skip to content

Commit

Permalink
feat: tailwind-variantsの使い方をチェックするルールを追加
Browse files Browse the repository at this point in the history
  • Loading branch information
AtsushiM committed Jan 10, 2025
1 parent 71d56c7 commit 070271b
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/eslint-plugin-smarthr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
- [best-practice-for-date](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-date)
- [best-practice-for-layouts](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-layouts)
- [best-practice-for-remote-trigger-dialog](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-remote-trigger-dialog)
- [best-practice-for-tailwind-variants](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-tailwind-variants)
- [design-system-guideline-prohibit-double-icons](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/design-system-guideline-prohibit-double-icons)
- [format-import-path](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/format-import-path)
- [format-translate-component](https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/format-translate-component)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# smarthr/best-practice-for-tailwind-variants

- tailwind-variantsの記法をチェックするルールです
- 現状は以下のチェックを行います
- tailwind-variants(tv) のimport時の名称をtvに固定しているか (asなどでの名称変更の禁止)
- tv の実行結果を格納する変数名を統一 (styleGenerator、もしくはxxxStyleGenerator)
- tvで生成した関数の実行をuseMemo hook でメモ化しているか


## rules

```js
{
rules: {
'smarthr/best-practice-for-tailwind-variants': 'error', // 'warn', 'off'
},
}
```

## ❌ Incorrect

```jsx
import { tv as hoge } from 'tailwind-variants'

const xxx = tv({
...
})

...

const style = xxx()
```

## ✅ Correct

```jsx
import { tv } from 'tailwind-variants'

const styleGenerator = tv({
...
})

...

const style = useMemo(() => styleGenerator(), [])
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
const SCHEMA = []

const TV_COMPONENTS_METHOD = 'tv'
const TV_COMPONENTS = 'tailwind-variants'
const TV_RESULT_CONST_NAME_REGEX = /(S|s)tyleGenerator$/

const findValidImportNameNode = (s) => s.type === 'ImportSpecifier' && s.local.name === TV_COMPONENTS_METHOD

const checkImportTailwindVariants = (node, context) => {
}
const findNodeHasId = (node) => {
if (node.id) {
return node
}

if (node.parent) {
return findNodeHasId(node.parent)
}

return null
}
const findNodeUseMemo = (node) => {
if (node.type === 'CallExpression' && node.callee.name === 'useMemo') {
return node
}

if (node.parent) {
return findNodeUseMemo(node.parent)
}

return null
}

/**
* @type {import('@typescript-eslint/utils').TSESLint.RuleModule<''>}
*/
module.exports = {
meta: {
type: 'problem',
schema: SCHEMA,
},
create(context) {
return {
ImportDeclaration: (node) => {
if (node.source.value === TV_COMPONENTS) {
if (!node.specifiers.some(findValidImportNameNode)) {
context.report({
node,
message: `${TV_COMPONENTS} をimportする際は、名称が"${TV_COMPONENTS_METHOD}" となるようにしてください。例: "import { ${TV_COMPONENTS_METHOD} } from '${TV_COMPONENTS}'"`,
});
}
}
},
CallExpression: (node) => {
if (node.callee.name === TV_COMPONENTS_METHOD) {
const idNode = findNodeHasId(node.parent)

if (idNode && !TV_RESULT_CONST_NAME_REGEX.test(idNode.id.name)) {
context.report({
node: idNode,
message: `${TV_COMPONENTS_METHOD}の実行結果を格納する変数名は "${idNode.id.name}" ではなく "${TV_RESULT_CONST_NAME_REGEX}"にmatchする名称に統一してください。`,
});
}
} else if (TV_RESULT_CONST_NAME_REGEX.test(node.callee.name)) {
const useMemoNode = findNodeUseMemo(node.parent)

if (!useMemoNode) {
context.report({
node,
message: `"${node.callee.name}" を実行する際、useMemoでラップし、メモ化してください`,
});
}
}
},
}
},
}
module.exports.schema = SCHEMA
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const rule = require('../rules/best-practice-for-tailwind-variants')
const RuleTester = require('eslint').RuleTester

const ruleTester = new RuleTester({
languageOptions: {
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
},
})

ruleTester.run('best-practice-for-button-element', rule, {
valid: [
{ code: `import { tv } from 'tailwind-variants'` },
{ code: `const styleGenerator = tv()` },
{ code: `const xxxStyleGenerator = tv()` },
{ code: `const hoge = useMemo(() => styleGenerator(), [])` },
{ code: `const xxx = useMemo(() => hogeStyleGenerator(), [])` },
],
invalid: [
{ code: `import { tv as hoge } from 'tailwind-variants'`, errors: [ { message: `tailwind-variants をimportする際は、名称が"tv" となるようにしてください。例: "import { tv } from 'tailwind-variants'"` } ] },
{ code: `const hoge = tv()`, errors: [ { message: `tvの実行結果を格納する変数名は "hoge" ではなく "/(S|s)tyleGenerator$/"にmatchする名称に統一してください。` } ] },
{ code: `const hoge = styleGenerator()`, errors: [ { message: `"styleGenerator" を実行する際、useMemoでラップし、メモ化してください` } ] },
{ code: `const hoge = hogeStyleGenerator()`, errors: [ { message: `"hogeStyleGenerator" を実行する際、useMemoでラップし、メモ化してください` } ] },
]
})

0 comments on commit 070271b

Please sign in to comment.