-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: tailwind-variantsの使い方をチェックするルールを追加
- Loading branch information
Showing
4 changed files
with
153 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 46 additions & 0 deletions
46
packages/eslint-plugin-smarthr/rules/best-practice-for-tailwind-variants/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(), []) | ||
``` |
78 changes: 78 additions & 0 deletions
78
packages/eslint-plugin-smarthr/rules/best-practice-for-tailwind-variants/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
28 changes: 28 additions & 0 deletions
28
packages/eslint-plugin-smarthr/test/best-practice-for-tailwind-variants.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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でラップし、メモ化してください` } ] }, | ||
] | ||
}) |