Skip to content

Commit e8a0943

Browse files
fix: support for JSXExpressionContainer containing Literal
1 parent 14b5f91 commit e8a0943

File tree

7 files changed

+261
-7
lines changed

7 files changed

+261
-7
lines changed

lib/rules/classnames-order.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -220,9 +220,10 @@ module.exports = {
220220
let suffix = '';
221221
let trim = false;
222222
if (arg === null) {
223-
originalClassNamesValue = node.value.value;
224-
start = node.value.range[0] + 1;
225-
end = node.value.range[1] - 1;
223+
originalClassNamesValue = astUtil.extractValueFromNode(node);
224+
const range = astUtil.extractRangeFromNode(node);
225+
start = range[0] + 1;
226+
end = range[1] - 1;
226227
} else {
227228
switch (arg.type) {
228229
case 'TemplateLiteral':

lib/util/ast.js

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,15 @@ function isVueLiteralAttributeValue(node) {
4747
* @returns {Boolean}
4848
*/
4949
function isLiteralAttributeValue(node) {
50-
if (node.value && node.value.type === 'Literal') {
51-
// No support for dynamic or conditional...
52-
return !/\{|\?|\}/.test(node.value.value);
50+
if (node.value) {
51+
switch (node.value.type) {
52+
case 'Literal':
53+
// No support for dynamic or conditional...
54+
return !/\{|\?|\}/.test(node.value.value);
55+
case 'JSXExpressionContainer':
56+
// className={"..."}
57+
return node.value.expression.type === 'Literal';
58+
}
5359
}
5460
return false;
5561
}
@@ -90,6 +96,24 @@ function isValidVueAttribute(node) {
9096
return true;
9197
}
9298

99+
function extractRangeFromNode(node) {
100+
switch (node.value.type) {
101+
case 'JSXExpressionContainer':
102+
return node.value.expression.range;
103+
default:
104+
return node.value.range;
105+
}
106+
}
107+
108+
function extractValueFromNode(node) {
109+
switch (node.value.type) {
110+
case 'JSXExpressionContainer':
111+
return node.value.expression.value;
112+
default:
113+
return node.value.value;
114+
}
115+
}
116+
93117
/**
94118
* Inspect and parse an abstract syntax node and run a callback function
95119
*
@@ -104,7 +128,7 @@ function parseNodeRecursive(node, arg, cb, skipConditional = false, isolate = fa
104128
let originalClassNamesValue;
105129
let classNames;
106130
if (arg === null) {
107-
originalClassNamesValue = node.value.value;
131+
originalClassNamesValue = extractValueFromNode(node);
108132
classNames = attrUtil.getClassNamesFromAttribute(originalClassNamesValue, true);
109133
classNames = removeDuplicatesFromArray(classNames);
110134
if (classNames.length === 0) {
@@ -172,6 +196,8 @@ function getTemplateElementSuffix(text, raw) {
172196
}
173197

174198
module.exports = {
199+
extractRangeFromNode,
200+
extractValueFromNode,
175201
isValidJSXAttribute,
176202
isValidVueAttribute,
177203
parseNodeRecursive,

package-lock.json

Lines changed: 123 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@
3232
"@tailwindcss/aspect-ratio": "^0.2.0",
3333
"@tailwindcss/line-clamp": "^0.2.0",
3434
"@tailwindcss/typography": "^0.4.0",
35+
"@typescript-eslint/parser": "^4.33.0",
3536
"eslint": "^7.1.0",
3637
"mocha": "^7.2.0",
38+
"typescript": "4.3.5",
3739
"vue-eslint-parser": "^7.6.0"
3840
},
3941
"engines": {

tests/lib/rules/classnames-order.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,48 @@ ruleTester.run("classnames-order", rule, {
179179
},
180180
],
181181
invalid: [
182+
{
183+
code: `
184+
export interface FakePropsInterface {
185+
readonly name?: string;
186+
}
187+
188+
function Fake({
189+
name = 'yolo'
190+
}: FakeProps) {
191+
192+
return (
193+
<>
194+
<h1 className={"absolute bottom-0 w-full flex flex-col"}>Welcome {name}</h1>
195+
<p>Bye {name}</p>
196+
</>
197+
);
198+
}
199+
200+
export default Fake;
201+
`,
202+
output: `
203+
export interface FakePropsInterface {
204+
readonly name?: string;
205+
}
206+
207+
function Fake({
208+
name = 'yolo'
209+
}: FakeProps) {
210+
211+
return (
212+
<>
213+
<h1 className={"flex absolute bottom-0 flex-col w-full"}>Welcome {name}</h1>
214+
<p>Bye {name}</p>
215+
</>
216+
);
217+
}
218+
219+
export default Fake;
220+
`,
221+
parser: require.resolve("@typescript-eslint/parser"),
222+
errors: errors,
223+
},
182224
{
183225
code: `<div class="sm:w-6 container w-12">Classnames will be ordered</div>`,
184226
output: `<div class="container w-12 sm:w-6">Classnames will be ordered</div>`,

tests/lib/rules/no-contradicting-classname.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,36 @@ ruleTester.run("no-contradicting-classname", rule, {
157157
],
158158

159159
invalid: [
160+
{
161+
code: `
162+
export interface FakePropsInterface {
163+
readonly name?: string;
164+
}
165+
166+
function Fake({
167+
name = 'yolo'
168+
}: FakeProps) {
169+
170+
return (
171+
<>
172+
<h1 className={"container w-1 w-2"}>Welcome {name}</h1>
173+
<p>Bye {name}</p>
174+
</>
175+
);
176+
}
177+
178+
export default Fake;
179+
`,
180+
parser: require.resolve("@typescript-eslint/parser"),
181+
errors: [
182+
{
183+
messageId: "conflictingClassnames",
184+
data: {
185+
classnames: "w-1, w-2",
186+
},
187+
},
188+
],
189+
},
160190
{
161191
code: '<div class="container w-1 w-2"></div>',
162192
errors: [

tests/lib/rules/no-custom-classname.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,36 @@ ruleTester.run("no-custom-classname", rule, {
198198
],
199199

200200
invalid: [
201+
{
202+
code: `
203+
export interface FakePropsInterface {
204+
readonly name?: string;
205+
}
206+
207+
function Fake({
208+
name = 'yolo'
209+
}: FakeProps) {
210+
211+
return (
212+
<>
213+
<h1 className={"w-12 my-custom"}>Welcome {name}</h1>
214+
<p>Bye {name}</p>
215+
</>
216+
);
217+
}
218+
219+
export default Fake;
220+
`,
221+
parser: require.resolve("@typescript-eslint/parser"),
222+
errors: [
223+
{
224+
messageId: "customClassnameDetected",
225+
data: {
226+
classname: "my-custom",
227+
},
228+
},
229+
],
230+
},
201231
{
202232
code: `<div class="w-12 my-custom">my-custom is not defined in Tailwind CSS!</div>`,
203233
errors: [

0 commit comments

Comments
 (0)