From 5a5db1b26741e0ec5c3a11c541a9b35dcade378e Mon Sep 17 00:00:00 2001 From: Nihal Gonsalves Date: Thu, 23 May 2024 05:27:55 -0400 Subject: [PATCH] fix: support tag.div`` and tag(Component)`` (#302) --- lib/rules/classnames-order.js | 2 +- .../enforces-negative-arbitrary-values.js | 2 +- lib/rules/enforces-shorthand.js | 7 +- lib/rules/migration-from-tailwind-2.js | 2 +- lib/rules/no-arbitrary-value.js | 2 +- lib/rules/no-contradicting-classname.js | 2 +- lib/rules/no-custom-classname.js | 2 +- package-lock.json | 2 +- package.json | 2 +- tests/lib/rules/arbitrary-values.js | 9 + tests/lib/rules/classnames-order.js | 168 +++++++++--------- .../enforces-negative-arbitrary-values.js | 9 + tests/lib/rules/enforces-shorthand.js | 10 ++ tests/lib/rules/migration-from-tailwind-2.js | 18 ++ tests/lib/rules/no-arbitrary-value.js | 9 + tests/lib/rules/no-contradicting-classname.js | 104 +++++------ tests/lib/rules/no-custom-classname.js | 102 +++++------ 17 files changed, 256 insertions(+), 196 deletions(-) diff --git a/lib/rules/classnames-order.js b/lib/rules/classnames-order.js index d3782d7..e7e3573 100644 --- a/lib/rules/classnames-order.js +++ b/lib/rules/classnames-order.js @@ -235,7 +235,7 @@ module.exports = { TextAttribute: attributeVisitor, CallExpression: callExpressionVisitor, TaggedTemplateExpression: function (node) { - if (!tags.includes(node.tag.name)) { + if (!tags.includes(node.tag.name ?? node.tag.object?.name ?? node.tag.callee?.name)) { return; } diff --git a/lib/rules/enforces-negative-arbitrary-values.js b/lib/rules/enforces-negative-arbitrary-values.js index 2ef01f3..1218993 100644 --- a/lib/rules/enforces-negative-arbitrary-values.js +++ b/lib/rules/enforces-negative-arbitrary-values.js @@ -179,7 +179,7 @@ module.exports = { TextAttribute: attributeVisitor, CallExpression: callExpressionVisitor, TaggedTemplateExpression: function (node) { - if (!tags.includes(node.tag.name)) { + if (!tags.includes(node.tag.name ?? node.tag.object?.name ?? node.tag.callee?.name)) { return; } parseForNegativeArbitraryClassNames(node, node.quasi); diff --git a/lib/rules/enforces-shorthand.js b/lib/rules/enforces-shorthand.js index 982d3a6..b147bfa 100644 --- a/lib/rules/enforces-shorthand.js +++ b/lib/rules/enforces-shorthand.js @@ -362,9 +362,8 @@ module.exports = { } else if (hasY || hasX) { const xOrY = hasX ? 'x' : 'y'; const xOrYType = getBodyByShorthand(targetGroups, classname.parentType, xOrY); - const patchedName = `${cls.variants}${important}${minus}${mergedConfig.prefix}${xOrYType}${ - absoluteVal.length ? '-' + absoluteVal : '' - }`; + const patchedName = `${cls.variants}${important}${minus}${mergedConfig.prefix}${xOrYType}${absoluteVal.length ? '-' + absoluteVal : '' + }`; const toBeReplaced = sameVariantAndValue .filter((c) => { const candidates = hasX ? ['l', 'r'] : ['t', 'b']; @@ -501,7 +500,7 @@ module.exports = { TextAttribute: attributeVisitor, CallExpression: callExpressionVisitor, TaggedTemplateExpression: function (node) { - if (!tags.includes(node.tag.name)) { + if (!tags.includes(node.tag.name ?? node.tag.object?.name ?? node.tag.callee?.name)) { return; } diff --git a/lib/rules/migration-from-tailwind-2.js b/lib/rules/migration-from-tailwind-2.js index 434d102..5de0f0f 100644 --- a/lib/rules/migration-from-tailwind-2.js +++ b/lib/rules/migration-from-tailwind-2.js @@ -290,7 +290,7 @@ module.exports = { TextAttribute: attributeVisitor, CallExpression: callExpressionVisitor, TaggedTemplateExpression: function (node) { - if (!tags.includes(node.tag.name)) { + if (!tags.includes(node.tag.name ?? node.tag.object?.name ?? node.tag.callee?.name)) { return; } parseForObsoleteClassNames(node, node.quasi); diff --git a/lib/rules/no-arbitrary-value.js b/lib/rules/no-arbitrary-value.js index eac5e25..93142d8 100644 --- a/lib/rules/no-arbitrary-value.js +++ b/lib/rules/no-arbitrary-value.js @@ -179,7 +179,7 @@ module.exports = { TextAttribute: attributeVisitor, CallExpression: callExpressionVisitor, TaggedTemplateExpression: function (node) { - if (!tags.includes(node.tag.name)) { + if (!tags.includes(node.tag.name ?? node.tag.object?.name ?? node.tag.callee?.name)) { return; } parseForArbitraryValues(node, node.quasi); diff --git a/lib/rules/no-contradicting-classname.js b/lib/rules/no-contradicting-classname.js index 5395bb9..c7ce96c 100644 --- a/lib/rules/no-contradicting-classname.js +++ b/lib/rules/no-contradicting-classname.js @@ -203,7 +203,7 @@ module.exports = { TextAttribute: attributeVisitor, CallExpression: callExpressionVisitor, TaggedTemplateExpression: function (node) { - if (!tags.includes(node.tag.name)) { + if (!tags.includes(node.tag.name ?? node.tag.object?.name ?? node.tag.callee?.name)) { return; } diff --git a/lib/rules/no-custom-classname.js b/lib/rules/no-custom-classname.js index aceb171..97216be 100644 --- a/lib/rules/no-custom-classname.js +++ b/lib/rules/no-custom-classname.js @@ -182,7 +182,7 @@ module.exports = { TextAttribute: attributeVisitor, CallExpression: callExpressionVisitor, TaggedTemplateExpression: function (node) { - if (!tags.includes(node.tag.name)) { + if (!tags.includes(node.tag.name ?? node.tag.object?.name ?? node.tag.callee?.name)) { return; } astUtil.parseNodeRecursive(node, node.quasi, parseForCustomClassNames, false, false, ignoredKeys); diff --git a/package-lock.json b/package-lock.json index be3ee60..ee9b03a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,7 +29,7 @@ "vue-eslint-parser": "^9.4.2" }, "engines": { - "node": ">=12.13.0" + "node": ">=14.0.0" }, "peerDependencies": { "tailwindcss": "^3.4.0" diff --git a/package.json b/package.json index 783a0a5..0edc529 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ }, "packageManager": "npm@10.2.5+sha256.8002e3e7305d2abd4016e1368af48d49b066c269079eeb10a56e7d6598acfdaa", "engines": { - "node": ">=12.13.0" + "node": ">=14.0.0" }, "license": "MIT" } diff --git a/tests/lib/rules/arbitrary-values.js b/tests/lib/rules/arbitrary-values.js index 70e47c4..2416317 100644 --- a/tests/lib/rules/arbitrary-values.js +++ b/tests/lib/rules/arbitrary-values.js @@ -677,5 +677,14 @@ ruleTester.run("arbitrary-values", rule, { options: config, errors: generateErrors("stroke-[angle:var(--some)]"), }, + ...(['myTag', 'myTag.subTag', 'myTag(SomeComponent)'].map(tag => ({ + code: `${tag}\`stroke-[var(--some)] stroke-['yolo'] stroke-[angle:var(--some)]\``, + errors: generateErrors("stroke-[angle:var(--some)]"), + options: [ + { + tags: ["myTag"], + }, + ], + }))), ], }); diff --git a/tests/lib/rules/classnames-order.js b/tests/lib/rules/classnames-order.js index c4bba5d..032c1f4 100644 --- a/tests/lib/rules/classnames-order.js +++ b/tests/lib/rules/classnames-order.js @@ -187,8 +187,8 @@ ruleTester.run("classnames-order", rule, { }, }, }, - { - code: `myTag\` + ...(['myTag', 'myTag.subTag', 'myTag(SomeComponent)'].map(tag => ({ + code: `${tag}\` container flex w-12 @@ -200,7 +200,7 @@ ruleTester.run("classnames-order", rule, { tags: ["myTag"], }, ], - }, + }))), { code: `
Number values
`, settings: { @@ -699,86 +699,88 @@ ruleTester.run("classnames-order", rule, { `, errors: generateErrors(2), }, - { - code: ` - myTag\` - invalid - sm:w-6 - container - w-12 - flex - lg:w-4 - \`;`, - output: ` - myTag\` - invalid - container - flex - w-12 - sm:w-6 - lg:w-4 - \`;`, - options: [ - { - tags: ["myTag"], - }, - ], - errors: errors, - }, - { - code: ` - const buttonClasses = myTag\` - \${fullWidth ? "w-12" : "w-6"} - container - \${fullWidth ? "sm:w-8" : "sm:w-4"} - lg:w-9 - flex - \${hasError && "bg-red"} - \`;`, - output: ` - const buttonClasses = myTag\` - \${fullWidth ? "w-12" : "w-6"} - container - \${fullWidth ? "sm:w-8" : "sm:w-4"} - flex - lg:w-9 - \${hasError && "bg-red"} - \`;`, - options: [ - { - tags: ["myTag"], - }, - ], - errors: errors, - }, - { - code: ` - const buttonClasses = myTag\` - \${fullWidth ? "w-12" : "w-6"} - flex - container - \${fullWidth ? "sm:w-7" : "sm:w-4"} - lg:py-4 - sm:py-6 - \${hasError && "bg-red"} - \`;`, - output: ` - const buttonClasses = myTag\` - \${fullWidth ? "w-12" : "w-6"} - container - flex - \${fullWidth ? "sm:w-7" : "sm:w-4"} - sm:py-6 - lg:py-4 - \${hasError && "bg-red"} - \`;`, - options: [ - { - tags: ["myTag"], - }, - ], - errors: generateErrors(2), - }, + ...(['myTag', 'myTag.subTag', 'myTag(SomeComponent)'].flatMap(tag => ([ + { + code: ` + ${tag}\` + invalid + sm:w-6 + container + w-12 + flex + lg:w-4 + \`;`, + output: ` + ${tag}\` + invalid + container + flex + w-12 + sm:w-6 + lg:w-4 + \`;`, + options: [ + { + tags: ["myTag"], + }, + ], + errors: errors, + }, + { + code: ` + const buttonClasses = ${tag}\` + \${fullWidth ? "w-12" : "w-6"} + container + \${fullWidth ? "sm:w-8" : "sm:w-4"} + lg:w-9 + flex + \${hasError && "bg-red"} + \`;`, + output: ` + const buttonClasses = ${tag}\` + \${fullWidth ? "w-12" : "w-6"} + container + \${fullWidth ? "sm:w-8" : "sm:w-4"} + flex + lg:w-9 + \${hasError && "bg-red"} + \`;`, + options: [ + { + tags: ["myTag"], + }, + ], + errors: errors, + }, + { + code: ` + const buttonClasses = ${tag}\` + \${fullWidth ? "w-12" : "w-6"} + flex + container + \${fullWidth ? "sm:w-7" : "sm:w-4"} + lg:py-4 + sm:py-6 + \${hasError && "bg-red"} + \`;`, + output: ` + const buttonClasses = ${tag}\` + \${fullWidth ? "w-12" : "w-6"} + container + flex + \${fullWidth ? "sm:w-7" : "sm:w-4"} + sm:py-6 + lg:py-4 + \${hasError && "bg-red"} + \`;`, + options: [ + { + tags: ["myTag"], + }, + ], + errors: generateErrors(2), + }, + ]))), { code: ` classnames([ diff --git a/tests/lib/rules/enforces-negative-arbitrary-values.js b/tests/lib/rules/enforces-negative-arbitrary-values.js index f1433c3..1fd9df2 100644 --- a/tests/lib/rules/enforces-negative-arbitrary-values.js +++ b/tests/lib/rules/enforces-negative-arbitrary-values.js @@ -140,5 +140,14 @@ ruleTester.run("enforces-negative-arbitrary-values", rule, { code: `
support named group/peer syntax
`, errors: generateErrors("group/edit:-inset-[1px]"), }, + ...(['myTag', 'myTag.subTag', 'myTag(SomeComponent)'].map(tag => ({ + code: `${tag}\`-my-[1px] -mx-[1px]\``, + errors: generateErrors("-my-[1px] -mx-[1px]"), + options: [ + { + tags: ["myTag"], + }, + ], + }))), ], }); diff --git a/tests/lib/rules/enforces-shorthand.js b/tests/lib/rules/enforces-shorthand.js index 00ebbee..30ecb43 100644 --- a/tests/lib/rules/enforces-shorthand.js +++ b/tests/lib/rules/enforces-shorthand.js @@ -800,5 +800,15 @@ ruleTester.run("shorthands", rule, { errors: [generateError(["h-custom", "w-custom"], "size-custom")], options: customSpacingOnlyOptions, }, + ...["myTag", "myTag.subTag", "myTag(SomeComponent)"].map((tag) => ({ + code: `${tag}\`overflow-hidden text-ellipsis whitespace-nowrap text-white text-xl\``, + output: `${tag}\`truncate text-white text-xl\``, + errors: [generateError(["overflow-hidden", "text-ellipsis", "whitespace-nowrap"], "truncate")], + options: [ + { + tags: ["myTag"], + }, + ], + })), ], }); diff --git a/tests/lib/rules/migration-from-tailwind-2.js b/tests/lib/rules/migration-from-tailwind-2.js index 9bf133f..0ada3e7 100644 --- a/tests/lib/rules/migration-from-tailwind-2.js +++ b/tests/lib/rules/migration-from-tailwind-2.js @@ -382,5 +382,23 @@ ruleTester.run("migration-from-tailwind-2", rule, { filename: "test.vue", parser: require.resolve("vue-eslint-parser"), }, + ...(['myTag', 'myTag.subTag', 'myTag(SomeComponent)'].map(tag => ({ + code: `${tag}\`flex-grow-0\``, + output: `${tag}\`grow-0\``, + errors: [ + { + messageId: "classnameChanged", + data: { + deprecated: "flex-grow-0", + updated: "grow-0", + }, + }, + ], + options: [ + { + tags: ["myTag"], + }, + ], + }))), ], }); diff --git a/tests/lib/rules/no-arbitrary-value.js b/tests/lib/rules/no-arbitrary-value.js index f564813..870c892 100644 --- a/tests/lib/rules/no-arbitrary-value.js +++ b/tests/lib/rules/no-arbitrary-value.js @@ -160,5 +160,14 @@ ruleTester.run("no-arbitrary-value", rule, { code: `
Dynamic viewport units
`, errors: generateErrors(["min-h-[75dvh]"]), }, + ...(['myTag', 'myTag.subTag', 'myTag(SomeComponent)'].map(tag => ({ + code: `${tag}\`w-[100px]\``, + errors: generateErrors("w-[100px]"), + options: [ + { + tags: ["myTag"], + }, + ], + }))), ], }); diff --git a/tests/lib/rules/no-contradicting-classname.js b/tests/lib/rules/no-contradicting-classname.js index 9996f81..b9db628 100644 --- a/tests/lib/rules/no-contradicting-classname.js +++ b/tests/lib/rules/no-contradicting-classname.js @@ -135,9 +135,9 @@ ruleTester.run("no-contradicting-classname", rule, { `, options: config, }, - { + ...(['myTag', 'myTag.subTag', 'myTag(SomeComponent)'].map(tag => ({ code: ` - myTag\` + ${tag}\` text-white rounded-md py-5 @@ -165,7 +165,7 @@ ruleTester.run("no-contradicting-classname", rule, { tags: ["myTag"], }, ], - }, + }))), { code: `
@@ -441,54 +441,56 @@ ruleTester.run("no-contradicting-classname", rule, { );`, errors: generateErrors(["p-2 p-4", "w-1 w-2", "py-1 py-2", "px-2 px-4"]), }, - { - code: ` - myTag\` - invalid bis - sm:w-6 - w-8 - container - w-12 - flex - lg:w-4 - \`;`, - options: [{ tags: ["myTag"] }], - errors: generateErrors("w-8 w-12"), - }, - { - code: ` - myTag\` - px-2 - px-4 - \${ - !isDisabled && - \` - py-1 - py-2 - \` - } - \${ - isDisabled && - \` - w-1 - w-2 - \` - } - \` - `, - options: [{ tags: ["myTag"] }], - errors: generateErrors(["py-1 py-2", "w-1 w-2", "px-2 px-4"]), - }, - { - code: `myTag\`\${enabled && "px-2 px-0"}\``, - options: [{ tags: ["myTag"] }], - errors: generateErrors("px-2 px-0"), - }, - { - code: `myTag\`\${enabled ? "px-2 px-0" : ""}\``, - options: [{ tags: ["myTag"] }], - errors: generateErrors("px-2 px-0"), - }, + ...(['myTag', 'myTag.subTag', 'myTag(SomeComponent)'].flatMap(tag => [ + { + code: ` + ${tag}\` + invalid bis + sm:w-6 + w-8 + container + w-12 + flex + lg:w-4 + \`;`, + options: [{ tags: ["myTag"] }], + errors: generateErrors("w-8 w-12"), + }, + { + code: ` + ${tag}\` + px-2 + px-4 + \${ + !isDisabled && + \` + py-1 + py-2 + \` + } + \${ + isDisabled && + \` + w-1 + w-2 + \` + } + \` + `, + options: [{ tags: ["myTag"] }], + errors: generateErrors(["py-1 py-2", "w-1 w-2", "px-2 px-4"]), + }, + { + code: `${tag}\`\${enabled && "px-2 px-0"}\``, + options: [{ tags: ["myTag"] }], + errors: generateErrors("px-2 px-0"), + }, + { + code: `${tag}\`\${enabled ? "px-2 px-0" : ""}\``, + options: [{ tags: ["myTag"] }], + errors: generateErrors("px-2 px-0"), + }, + ])), { code: `
diff --git a/tests/lib/rules/no-custom-classname.js b/tests/lib/rules/no-custom-classname.js index 13ef669..58fd948 100644 --- a/tests/lib/rules/no-custom-classname.js +++ b/tests/lib/rules/no-custom-classname.js @@ -444,9 +444,9 @@ ruleTester.run("no-custom-classname", rule, { }, ], }, - { + ...(['myTag', 'myTag.subTag', 'myTag(SomeComponent)'].map(tag => ({ code: ` - myTag\` + ${tag}\` sm:w-6 w-8 container @@ -455,7 +455,7 @@ ruleTester.run("no-custom-classname", rule, { lg:w-4 \`;`, options: [{ tags: ["myTag"] }], - }, + }))), { code: `
@@ -1224,53 +1224,55 @@ ruleTester.run("no-custom-classname", rule, { ], errors: generateErrors("dark"), }, - { - code: ` - myTag\` - sm:w-6 - hello - w-8 - container - w-12 - world - flex - lg:w-4 - \`;`, - options: [{ tags: ["myTag"] }], - errors: generateErrors("hello world"), - }, - { - code: ` - myTag\` - px-4 - custom-1 - py-1 - \${ - !isDisabled && - \` - lg:focus:ring-1 - custom-2 - focus:ring-2 - \` - } - \${ - isDisabled && - \` - lg:opacity-25 - custom-3 - opacity-50 - \` - } - \` - `, - options: [{ tags: ["myTag"] }], - errors: generateErrors("custom-2 custom-3 custom-1"), - }, - { - code: `myTag\`custom-1 \${isDisabled ? "custom-2" : "m-4"}\``, - options: [{ tags: ["myTag"] }], - errors: generateErrors("custom-2 custom-1"), - }, + ...(['myTag', 'myTag.subTag', 'myTag(SomeComponent)'].flatMap(tag => ([ + { + code: ` + ${tag}\` + sm:w-6 + hello + w-8 + container + w-12 + world + flex + lg:w-4 + \`;`, + options: [{ tags: ["myTag"] }], + errors: generateErrors("hello world"), + }, + { + code: ` + ${tag}\` + px-4 + custom-1 + py-1 + \${ + !isDisabled && + \` + lg:focus:ring-1 + custom-2 + focus:ring-2 + \` + } + \${ + isDisabled && + \` + lg:opacity-25 + custom-3 + opacity-50 + \` + } + \` + `, + options: [{ tags: ["myTag"] }], + errors: generateErrors("custom-2 custom-3 custom-1"), + }, + { + code: `${tag}\`custom-1 \${isDisabled ? "custom-2" : "m-4"}\``, + options: [{ tags: ["myTag"] }], + errors: generateErrors("custom-2 custom-1"), + }, + ]))), { code: `