diff --git a/.gitignore b/.gitignore index 68b21f0..96e4deb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /node_modules .DS_Store +.idea diff --git a/README.md b/README.md index 785188b..1ee667c 100644 --- a/README.md +++ b/README.md @@ -181,6 +181,8 @@ All these settings have nice default values that are explained in each rules' do removeDuplicates: true, skipClassAttribute: false, whitelist: [], + tags: [], + classRegex: "^class(Name)?$" // can be modified to support custom attributes. E.g. "^tw$" for `twin.macro` }, }, } diff --git a/docs/rules/classnames-order.md b/docs/rules/classnames-order.md index 0ffe0ed..3e63a41 100644 --- a/docs/rules/classnames-order.md +++ b/docs/rules/classnames-order.md @@ -64,3 +64,7 @@ While, this will avoid linting the `class` and `className` attributes, it will s ### `tags` (default: `[]`) Optional, if you are using tagged templates, you should provide the tags in this array. + +### `classRegex` (default: `"^class(Name)?$"`) + +Optional, can be used to support custom attribues diff --git a/docs/rules/enforces-negative-arbitrary-values.md b/docs/rules/enforces-negative-arbitrary-values.md index 98ba2f0..1d44362 100644 --- a/docs/rules/enforces-negative-arbitrary-values.md +++ b/docs/rules/enforces-negative-arbitrary-values.md @@ -78,3 +78,7 @@ While, this will avoid linting the `class` and `className` attributes, it will s ### `tags` (default: `[]`) Optional, if you are using tagged templates, you should provide the tags in this array. + +### `classRegex` (default: `"^class(Name)?$"`) + +Optional, can be used to support custom attribues diff --git a/docs/rules/enforces-shorthand.md b/docs/rules/enforces-shorthand.md index ddc6c9e..80cb44e 100644 --- a/docs/rules/enforces-shorthand.md +++ b/docs/rules/enforces-shorthand.md @@ -71,6 +71,10 @@ While, this will avoid linting the `class` and `className` attributes, it will s Optional, if you are using tagged templates, you should provide the tags in this array. +### `classRegex` (default: `"^class(Name)?$"`) + +Optional, can be used to support custom attribues + ## Further Reading This rule will fix the issue for you. diff --git a/docs/rules/migration-from-tailwind-2.md b/docs/rules/migration-from-tailwind-2.md index ff45476..f0382cb 100644 --- a/docs/rules/migration-from-tailwind-2.md +++ b/docs/rules/migration-from-tailwind-2.md @@ -115,3 +115,7 @@ While, this will avoid linting the `class` and `className` attributes, it will s ### `tags` (default: `[]`) Optional, if you are using tagged templates, you should provide the tags in this array. + +### `classRegex` (default: `"^class(Name)?$"`) + +Optional, can be used to support custom attribues diff --git a/docs/rules/no-arbitrary-value.md b/docs/rules/no-arbitrary-value.md index 1fa4668..7022c6a 100644 --- a/docs/rules/no-arbitrary-value.md +++ b/docs/rules/no-arbitrary-value.md @@ -61,6 +61,10 @@ While, this will avoid linting the `class` and `className` attributes, it will s Optional, if you are using tagged templates, you should provide the tags in this array. +### `classRegex` (default: `"^class(Name)?$"`) + +Optional, can be used to support custom attribues + ## Further Reading This rule will not fix the issue for you because it cannot guess the correct class candidate. diff --git a/docs/rules/no-contradicting-classname.md b/docs/rules/no-contradicting-classname.md index 88d7e13..a9b21d5 100644 --- a/docs/rules/no-contradicting-classname.md +++ b/docs/rules/no-contradicting-classname.md @@ -61,6 +61,10 @@ While, this will avoid linting the `class` and `className` attributes, it will s Optional, if you are using tagged templates, you should provide the tags in this array. +### `classRegex` (default: `"^class(Name)?$"`) + +Optional, can be used to support custom attribues + ## Further Reading This rule will not fix the issue but will let you know about the issue. diff --git a/docs/rules/no-custom-classname.md b/docs/rules/no-custom-classname.md index d7fe602..ebc41e1 100644 --- a/docs/rules/no-custom-classname.md +++ b/docs/rules/no-custom-classname.md @@ -102,6 +102,10 @@ e.g. I want to allow custom classnames while checking the validity of the `text- ]; ``` +### `classRegex` (default: `"^class(Name)?$"`) + +Optional, can be used to support custom attribues + ## Further Reading This rule will not fix the issue but will let you know about the issue. diff --git a/lib/rules/classnames-order.js b/lib/rules/classnames-order.js index c877be7..d0ff88c 100644 --- a/lib/rules/classnames-order.js +++ b/lib/rules/classnames-order.js @@ -67,6 +67,7 @@ module.exports = { const skipClassAttribute = getOption(context, 'skipClassAttribute'); const tags = getOption(context, 'tags'); const twConfig = getOption(context, 'config'); + const classRegex = getOption(context, 'classRegex'); const removeDuplicates = getOption(context, 'removeDuplicates'); const mergedConfig = customConfig.resolve(twConfig); @@ -199,7 +200,7 @@ module.exports = { //---------------------------------------------------------------------- const attributeVisitor = function (node) { - if (!astUtil.isClassAttribute(node) || skipClassAttribute) { + if (!astUtil.isClassAttribute(node, classRegex) || skipClassAttribute) { return; } if (astUtil.isLiteralAttributeValue(node)) { diff --git a/lib/rules/enforces-negative-arbitrary-values.js b/lib/rules/enforces-negative-arbitrary-values.js index ad5b99f..01654c4 100644 --- a/lib/rules/enforces-negative-arbitrary-values.js +++ b/lib/rules/enforces-negative-arbitrary-values.js @@ -8,7 +8,6 @@ const docsUrl = require('../util/docsUrl'); const customConfig = require('../util/customConfig'); const astUtil = require('../util/ast'); const groupUtil = require('../util/groupMethods'); -const removeDuplicatesFromClassnamesAndWhitespaces = require('../util/removeDuplicatesFromClassnamesAndWhitespaces'); const getOption = require('../util/settings'); const parserUtil = require('../util/parser'); @@ -60,6 +59,7 @@ module.exports = { const skipClassAttribute = getOption(context, 'skipClassAttribute'); const tags = getOption(context, 'tags'); const twConfig = getOption(context, 'config'); + const classRegex = getOption(context, 'classRegex'); const mergedConfig = customConfig.resolve(twConfig); @@ -145,7 +145,7 @@ module.exports = { //---------------------------------------------------------------------- const attributeVisitor = function (node) { - if (!astUtil.isClassAttribute(node) || skipClassAttribute) { + if (!astUtil.isClassAttribute(node, classRegex) || skipClassAttribute) { return; } if (astUtil.isLiteralAttributeValue(node)) { diff --git a/lib/rules/enforces-shorthand.js b/lib/rules/enforces-shorthand.js index 97c8e33..c3aee49 100644 --- a/lib/rules/enforces-shorthand.js +++ b/lib/rules/enforces-shorthand.js @@ -60,6 +60,7 @@ module.exports = { const skipClassAttribute = getOption(context, 'skipClassAttribute'); const tags = getOption(context, 'tags'); const twConfig = getOption(context, 'config'); + const classRegex = getOption(context, 'classRegex'); const mergedConfig = customConfig.resolve(twConfig); @@ -374,7 +375,7 @@ module.exports = { //---------------------------------------------------------------------- const attributeVisitor = function (node) { - if (!astUtil.isClassAttribute(node) || skipClassAttribute) { + if (!astUtil.isClassAttribute(node, classRegex) || skipClassAttribute) { return; } if (astUtil.isLiteralAttributeValue(node)) { diff --git a/lib/rules/migration-from-tailwind-2.js b/lib/rules/migration-from-tailwind-2.js index 46d3e59..9a43a22 100644 --- a/lib/rules/migration-from-tailwind-2.js +++ b/lib/rules/migration-from-tailwind-2.js @@ -65,6 +65,7 @@ module.exports = { const skipClassAttribute = getOption(context, 'skipClassAttribute'); const tags = getOption(context, 'tags'); const twConfig = getOption(context, 'config'); + const classRegex = getOption(context, 'classRegex'); const mergedConfig = customConfig.resolve(twConfig); @@ -256,7 +257,7 @@ module.exports = { //---------------------------------------------------------------------- const attributeVisitor = function (node) { - if (!astUtil.isClassAttribute(node) || skipClassAttribute) { + if (!astUtil.isClassAttribute(node, classRegex) || skipClassAttribute) { return; } if (astUtil.isLiteralAttributeValue(node)) { diff --git a/lib/rules/no-arbitrary-value.js b/lib/rules/no-arbitrary-value.js index 7b766e0..6287587 100644 --- a/lib/rules/no-arbitrary-value.js +++ b/lib/rules/no-arbitrary-value.js @@ -59,6 +59,7 @@ module.exports = { const skipClassAttribute = getOption(context, 'skipClassAttribute'); const tags = getOption(context, 'tags'); const twConfig = getOption(context, 'config'); + const classRegex = getOption(context, 'classRegex'); const mergedConfig = customConfig.resolve(twConfig); @@ -144,7 +145,7 @@ module.exports = { //---------------------------------------------------------------------- const attributeVisitor = function (node) { - if (!astUtil.isClassAttribute(node) || skipClassAttribute) { + if (!astUtil.isClassAttribute(node, classRegex) || skipClassAttribute) { return; } if (astUtil.isLiteralAttributeValue(node)) { diff --git a/lib/rules/no-contradicting-classname.js b/lib/rules/no-contradicting-classname.js index f9a9418..815c8c6 100644 --- a/lib/rules/no-contradicting-classname.js +++ b/lib/rules/no-contradicting-classname.js @@ -60,6 +60,7 @@ module.exports = { const skipClassAttribute = getOption(context, 'skipClassAttribute'); const tags = getOption(context, 'tags'); const twConfig = getOption(context, 'config'); + const classRegex = getOption(context, 'classRegex'); const mergedConfig = customConfig.resolve(twConfig); @@ -153,7 +154,7 @@ module.exports = { //---------------------------------------------------------------------- const attributeVisitor = function (node) { - if (!astUtil.isClassAttribute(node) || skipClassAttribute) { + if (!astUtil.isClassAttribute(node, classRegex) || skipClassAttribute) { return; } if (astUtil.isLiteralAttributeValue(node)) { diff --git a/lib/rules/no-custom-classname.js b/lib/rules/no-custom-classname.js index ff578f5..697c561 100644 --- a/lib/rules/no-custom-classname.js +++ b/lib/rules/no-custom-classname.js @@ -82,6 +82,7 @@ module.exports = { const cssFiles = getOption(context, 'cssFiles'); const cssFilesRefreshRate = getOption(context, 'cssFilesRefreshRate'); const whitelist = getOption(context, 'whitelist'); + const classRegex = getOption(context, 'classRegex'); const mergedConfig = customConfig.resolve(twConfig); const contextFallback = // Set the created contextFallback in the cache if it does not exist yet. @@ -138,7 +139,7 @@ module.exports = { //---------------------------------------------------------------------- const attributeVisitor = function (node) { - if (!astUtil.isClassAttribute(node) || skipClassAttribute) { + if (!astUtil.isClassAttribute(node, classRegex) || skipClassAttribute) { return; } if (astUtil.isLiteralAttributeValue(node)) { diff --git a/lib/util/ast.js b/lib/util/ast.js index ae62098..d9b91c7 100644 --- a/lib/util/ast.js +++ b/lib/util/ast.js @@ -14,9 +14,10 @@ const removeDuplicatesFromArray = require('./removeDuplicatesFromArray'); * Find out if node is `class` or `className` * * @param {ASTNode} node The AST node being checked + * @param {String} classRegex Regex to test the attribute that is being checked against * @returns {Boolean} */ -function isClassAttribute(node) { +function isClassAttribute(node, classRegex) { if (!node.name) { return false; } @@ -28,7 +29,7 @@ function isClassAttribute(node) { default: name = node.name.name; } - return /^class(Name)?$/.test(name); + return new RegExp(classRegex).test(name); } /** @@ -81,10 +82,11 @@ function isLiteralAttributeValue(node) { * Find out if the node is a valid candidate for our rules * * @param {ASTNode} node The AST node being checked + * @param {String} classRegex Regex to test the attribute that is being checked against * @returns {Boolean} */ -function isValidJSXAttribute(node) { - if (!isClassAttribute(node)) { +function isValidJSXAttribute(node, classRegex) { + if (!isClassAttribute(node, classRegex)) { // Only run for class[Name] attributes return false; } diff --git a/lib/util/settings.js b/lib/util/settings.js index 3c23baa..b22f4ba 100644 --- a/lib/util/settings.js +++ b/lib/util/settings.js @@ -26,6 +26,8 @@ function getOption(context, name) { return []; case 'whitelist': return []; + case 'classRegex': + return "^class(Name)?$" } } diff --git a/tests/lib/rules/classnames-order.js b/tests/lib/rules/classnames-order.js index 59e253a..2c0e591 100644 --- a/tests/lib/rules/classnames-order.js +++ b/tests/lib/rules/classnames-order.js @@ -75,6 +75,14 @@ ruleTester.run("classnames-order", rule, { { code: `
Simple, basic
`, }, + { + code: `
Simple, using 'tw' prop
`, + options: [ + { + classRegex: "^tw$", + }, + ], + }, { code: "
ctl + exp
", }, @@ -101,6 +109,14 @@ ruleTester.run("classnames-order", rule, { { code: `
Extra space at the end
`, }, + { + code: `
Extra space at the end, but with 'tw' prop
`, + options: [ + { + classRegex: "^tw$", + }, + ], + }, { code: `
'p', then 'py' then 'px'
`, }, @@ -310,6 +326,16 @@ ruleTester.run("classnames-order", rule, { output: `
Enhancing readability
`, errors: errors, }, + { + code: `
Enhancing readability with 'tw' prop
`, + output: `
Enhancing readability with 'tw' prop
`, + options: [ + { + classRegex: "^tw$", + }, + ], + errors: errors, + }, { code: `
:)...
`, output: `
:)...
`,