diff --git a/README.md b/README.md index b1546be..e9e2062 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,9 @@ If you enjoy my work you can: ## Latest changelog +- FIX: [Escaping special characters in the `prefix`](https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/73) +- FIX: [Formating HTML files](https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/85) is now possible using `@angular-eslint/template-parser` + - New feature: [crawling `ArrayExpression` elements and `ObjectExpression`](https://github.com/francoismassart/eslint-plugin-tailwindcss/pull/103), see [issue #99](https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/99) (by [matt-tingen](https://github.com/matt-tingen) 🙏) - New rule: [`no-arbitrary-value`](https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/89) which forbid using arbitrary values in classnames - New rule: [default for `cssFiles` option used by `no-custom-classname`](https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/37) diff --git a/lib/rules/classnames-order.js b/lib/rules/classnames-order.js index 7ae48b3..e557263 100644 --- a/lib/rules/classnames-order.js +++ b/lib/rules/classnames-order.js @@ -226,8 +226,13 @@ module.exports = { if (arg === null) { originalClassNamesValue = astUtil.extractValueFromNode(node); const range = astUtil.extractRangeFromNode(node); - start = range[0] + 1; - end = range[1] - 1; + if (node.type === 'TextAttribute') { + start = range[0]; + end = range[1]; + } else { + start = range[0] + 1; + end = range[1] - 1; + } } else { switch (arg.type) { case 'TemplateLiteral': @@ -341,13 +346,17 @@ module.exports = { //---------------------------------------------------------------------- // Public //---------------------------------------------------------------------- + + const attributeVisitor = function (node) { + if (!astUtil.isValidJSXAttribute(node)) { + return; + } + sortNodeArgumentValue(node); + }; + const scriptVisitor = { - JSXAttribute: function (node) { - if (!astUtil.isValidJSXAttribute(node)) { - return; - } - sortNodeArgumentValue(node); - }, + JSXAttribute: attributeVisitor, + TextAttribute: attributeVisitor, CallExpression: function (node) { if (callees.findIndex((name) => node.callee.name === name) === -1) { return; diff --git a/lib/rules/enforces-shorthand.js b/lib/rules/enforces-shorthand.js index 72168f9..16cd6d0 100644 --- a/lib/rules/enforces-shorthand.js +++ b/lib/rules/enforces-shorthand.js @@ -342,13 +342,16 @@ module.exports = { // Public //---------------------------------------------------------------------- + const attributeVisitor = function (node) { + if (!astUtil.isValidJSXAttribute(node)) { + return; + } + parseForShorthandCandidates(node); + }; + const scriptVisitor = { - JSXAttribute: function (node) { - if (!astUtil.isValidJSXAttribute(node)) { - return; - } - parseForShorthandCandidates(node); - }, + JSXAttribute: attributeVisitor, + TextAttribute: attributeVisitor, CallExpression: function (node) { if (callees.findIndex((name) => node.callee.name === name) === -1) { return; diff --git a/lib/rules/migration-from-tailwind-2.js b/lib/rules/migration-from-tailwind-2.js index d0d5ff9..71fc4a0 100644 --- a/lib/rules/migration-from-tailwind-2.js +++ b/lib/rules/migration-from-tailwind-2.js @@ -241,13 +241,16 @@ module.exports = { // Public //---------------------------------------------------------------------- + const attributeVisitor = function (node) { + if (!astUtil.isValidJSXAttribute(node)) { + return; + } + parseForObsoleteClassNames(node); + }; + const scriptVisitor = { - JSXAttribute: function (node) { - if (!astUtil.isValidJSXAttribute(node)) { - return; - } - parseForObsoleteClassNames(node); - }, + JSXAttribute: attributeVisitor, + TextAttribute: attributeVisitor, CallExpression: function (node) { if (callees.findIndex((name) => node.callee.name === name) === -1) { return; diff --git a/lib/rules/no-arbitrary-value.js b/lib/rules/no-arbitrary-value.js index 5721431..1ca9791 100644 --- a/lib/rules/no-arbitrary-value.js +++ b/lib/rules/no-arbitrary-value.js @@ -138,13 +138,16 @@ module.exports = { // Public //---------------------------------------------------------------------- + const attributeVisitor = function (node) { + if (!astUtil.isValidJSXAttribute(node)) { + return; + } + parseForArbitraryValues(node); + }; + const scriptVisitor = { - JSXAttribute: function (node) { - if (!astUtil.isValidJSXAttribute(node)) { - return; - } - parseForArbitraryValues(node); - }, + JSXAttribute: attributeVisitor, + TextAttribute: attributeVisitor, CallExpression: function (node) { if (callees.findIndex((name) => node.callee.name === name) === -1) { return; diff --git a/lib/rules/no-contradicting-classname.js b/lib/rules/no-contradicting-classname.js index bbcb21b..538642a 100644 --- a/lib/rules/no-contradicting-classname.js +++ b/lib/rules/no-contradicting-classname.js @@ -129,13 +129,16 @@ module.exports = { // Public //---------------------------------------------------------------------- + const attributeVisitor = function (node) { + if (!astUtil.isValidJSXAttribute(node)) { + return; + } + astUtil.parseNodeRecursive(node, null, parseForContradictingClassNames, true); + }; + const scriptVisitor = { - JSXAttribute: function (node) { - if (!astUtil.isValidJSXAttribute(node)) { - return; - } - astUtil.parseNodeRecursive(node, null, parseForContradictingClassNames, true); - }, + JSXAttribute: attributeVisitor, + TextAttribute: attributeVisitor, CallExpression: function (node) { if (callees.findIndex((name) => node.callee.name === name) === -1) { return; diff --git a/lib/rules/no-custom-classname.js b/lib/rules/no-custom-classname.js index 5abb5b6..ddd4314 100644 --- a/lib/rules/no-custom-classname.js +++ b/lib/rules/no-custom-classname.js @@ -125,13 +125,16 @@ module.exports = { // Public //---------------------------------------------------------------------- + const attributeVisitor = function (node) { + if (!astUtil.isValidJSXAttribute(node)) { + return; + } + astUtil.parseNodeRecursive(node, null, parseForCustomClassNames); + }; + const scriptVisitor = { - JSXAttribute: function (node) { - if (!astUtil.isValidJSXAttribute(node)) { - return; - } - astUtil.parseNodeRecursive(node, null, parseForCustomClassNames); - }, + JSXAttribute: attributeVisitor, + TextAttribute: attributeVisitor, CallExpression: function (node) { if (callees.findIndex((name) => node.callee.name === name) === -1) { return; diff --git a/lib/util/ast.js b/lib/util/ast.js index 21f5595..7f0bd42 100644 --- a/lib/util/ast.js +++ b/lib/util/ast.js @@ -4,6 +4,10 @@ 'use strict'; +// context.parserPath +// /.../eslint-plugin-tailwindcss/node_modules/espree/espree.js +// /.../eslint-plugin-tailwindcss/node_modules/@angular-eslint/template-parser/dist/index.js + const attrUtil = require('./attr'); const removeDuplicatesFromArray = require('./removeDuplicatesFromArray'); @@ -14,7 +18,18 @@ const removeDuplicatesFromArray = require('./removeDuplicatesFromArray'); * @returns {Boolean} */ function isClassAttribute(node) { - return node.name && /^class(Name)?$/.test(node.name.name); + if (!node.name) { + return false; + } + let name = ''; + switch (node.type) { + case 'TextAttribute': + name = node.name; + break; + default: + name = node.name.name; + } + return /^class(Name)?$/.test(name); } /** @@ -47,6 +62,9 @@ function isVueLiteralAttributeValue(node) { * @returns {Boolean} */ function isLiteralAttributeValue(node) { + if (node.type === 'TextAttribute' && node.name === 'class' && typeof node.value === 'string') { + return true; + } if (node.value) { switch (node.value.type) { case 'Literal': @@ -97,6 +115,9 @@ function isValidVueAttribute(node) { } function extractRangeFromNode(node) { + if (node.type === 'TextAttribute' && node.name === 'class') { + return [node.valueSpan.fullStart.offset, node.valueSpan.end.offset]; + } switch (node.value.type) { case 'JSXExpressionContainer': return node.value.expression.range; @@ -106,6 +127,9 @@ function extractRangeFromNode(node) { } function extractValueFromNode(node) { + if (node.type === 'TextAttribute' && node.name === 'class') { + return node.value; + } switch (node.value.type) { case 'JSXExpressionContainer': return node.value.expression.value; diff --git a/lib/util/groupMethods.js b/lib/util/groupMethods.js index 1475fd0..9f919c8 100644 --- a/lib/util/groupMethods.js +++ b/lib/util/groupMethods.js @@ -29,7 +29,7 @@ const length = require('./types/length'); * @returns {String} Escaped version */ function escapeSpecialChars(str) { - return str.replace(/[\-\.\/\(\)]/g, '\\$&'); + return str.replace(/\W/g, '\\$&'); } /** diff --git a/package-lock.json b/package-lock.json index 2bbe626..a2aee41 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,9 +1,25 @@ { "name": "eslint-plugin-tailwindcss", - "version": "3.3.0", + "version": "3.3.1-beta.0", "lockfileVersion": 1, "requires": true, "dependencies": { + "@angular-eslint/bundled-angular-compiler": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-13.0.1.tgz", + "integrity": "sha512-Eih9Kh0hxHO4+3in9mgjksQecym0p+3p+287y3LLihIc7gCkAO4xZeHGVGiC8qUX72PNUXkDlyskI9oHjK9Axw==", + "dev": true + }, + "@angular-eslint/template-parser": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-13.0.1.tgz", + "integrity": "sha512-GEJzVLS4Sb4UdurqaPD1/ucGhagGAQCp17CIgjpcXRwzxBZ9OLqbO/rx8diRbADp+1rceVq4BhADsg3VdsOsuw==", + "dev": true, + "requires": { + "@angular-eslint/bundled-angular-compiler": "13.0.1", + "eslint-scope": "^5.1.0" + } + }, "@babel/code-frame": { "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", diff --git a/package.json b/package.json index c0e90c7..f450f9f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-tailwindcss", - "version": "3.3.0", + "version": "3.3.1-beta.0", "description": "Rules enforcing best practices while using Tailwind CSS", "keywords": [ "eslint", @@ -29,6 +29,7 @@ "tailwindcss": "^3.0.7" }, "devDependencies": { + "@angular-eslint/template-parser": "^13.0.1", "@tailwindcss/aspect-ratio": "^0.4.0", "@tailwindcss/forms": "^0.4.0", "@tailwindcss/line-clamp": "^0.3.0", diff --git a/tests/lib/rules/classnames-order.js b/tests/lib/rules/classnames-order.js index 8a73b5a..5895ba3 100644 --- a/tests/lib/rules/classnames-order.js +++ b/tests/lib/rules/classnames-order.js @@ -661,5 +661,46 @@ ruleTester.run("classnames-order", rule, { })`, errors: errors, }, + { + code: ` +
+ `, + output: ` + + `, + errors: [...errors, ...errors], + parser: require.resolve("@angular-eslint/template-parser"), + }, + { + code: `