diff --git a/packages/eslint-plugin/lib/rules/class-order.js b/packages/eslint-plugin/lib/rules/class-order.js index 933315c1a..33b0986a4 100644 --- a/packages/eslint-plugin/lib/rules/class-order.js +++ b/packages/eslint-plugin/lib/rules/class-order.js @@ -123,8 +123,13 @@ module.exports = { const w = whitespaces[i] ?? '' const cls = orderedClassNames[i] validatedClassNamesValue += headSpace ? `${w}${cls}` : `${cls}${w}` - if (headSpace && tailSpace && i === orderedClassNames.length - 1) { - validatedClassNamesValue += whitespaces[whitespaces.length - 1] ?? '' + if (cls) { + if (!tailSpace && i === orderedClassNames.length - 1) { + validatedClassNamesValue = validatedClassNamesValue.replace(/\s+$/, '') + } + if (headSpace && tailSpace && i === orderedClassNames.length - 1) { + validatedClassNamesValue += whitespaces[whitespaces.length - 1] ?? '' + } } } diff --git a/packages/eslint-plugin/tests/order.test.js b/packages/eslint-plugin/tests/order.test.js index 1ac4088f1..a50a15cd8 100644 --- a/packages/eslint-plugin/tests/order.test.js +++ b/packages/eslint-plugin/tests/order.test.js @@ -4,6 +4,7 @@ const RuleTester = require('eslint').RuleTester new RuleTester({ parserOptions: { + ecmaVersion: 2019, ecmaFeatures: { jsx: true, }, @@ -18,12 +19,95 @@ new RuleTester({ }, { code: `
Simple, using 'test' prop
`, + settings: { + '@master/css': { + classMatching: '^test|class(Name)?$', + }, + }, + }, + { + code: '
ctl + exp
', + }, + { + code: '
ctl + var
', + }, + { + code: '
Space trim issue
', + }, + { + code: ` + ctl(\` + flex align-items:center justify-content:center + \${ variant === SpinnerVariant.OVERLAY && \`rounded bg:gray-40 b:2 px:4 z:60 \${widthClass} \${heightClass}\`} + \${ variant === SpinnerVariant.FULLSCREEN && + \`fixed bg:white@dark bottom:0 left:0 opacity:.6@dark px:4 right:0 top:0 z:60\` + } + \`)`, + }, + { + code: `
Simple quotes
`, + }, + { + code: `
Extra space at the end
`, + }, + { + code: `
Extra space at the end, but with 'tw' prop
`, + settings: { + '@master/css': { + classMatching: '^test|class(Name)?$', + }, + }, + }, + { + code: `
'p', then 'py' then 'px'
`, + }, + { + code: `ctl(\` + container + flex + w:12 + w:6@sm + w:4@lg + \`)`, + }, + { + code: `
Allowed arbitrary value
`, + }, + { + code: `
Stackable variants
`, + }, + { + code: `
clsx
`, options: [ { - classMatching: '^test$', + callees: ['clsx'], }, ], }, + { + code: `
Number values
`, + settings: { + '@master/css': { + config: { + styles: { zDialog: 'z:10000' } + } + } + } + }, + { + code: `
Extra spaces
`, + }, + { + code: ` +
Issue #131
+ `, + }, + { + code: `
No errors while typing
`, + }, + { + code: `
Do not treat full width space as class separator
`, + }, ], invalid: [ { @@ -35,6 +119,335 @@ new RuleTester({ code: ``, output: ``, errors: [{ messageId: 'invalidClassOrder' }], + }, + { + code: `
Enhancing readability with 'test' prop
`, + output: `
Enhancing readability with 'test' prop
`, + settings: { + '@master/css': { + classMatching: '^test|class(Name)?$', + }, + }, + errors: [{ messageId: 'invalidClassOrder' }], + }, + { + code: `
:)...
`, + output: `
:)...
`, + errors: [{ messageId: 'invalidClassOrder' }], + }, + { + code: 'ctl(`p:10 flex ${some}`)', + output: 'ctl(`flex p:10 ${some}`)', + errors: [{ messageId: 'invalidClassOrder' }], + }, + { + code: '
Space trim issue with fix
', + output: '
Space trim issue with fix
', + errors: [ + { messageId: 'invalidClassOrder' }, + { messageId: 'invalidClassOrder' } + ], + }, + { + code: `
Simple quotes
`, + output: `
Simple quotes
`, + errors: [{ messageId: 'invalidClassOrder' }], + }, + { + options: [ + { + removeDuplicates: false, + }, + ], + code: `
removeDuplicates
`, + output: `
removeDuplicates
`, + errors: [{ messageId: 'invalidClassOrder' }], + }, + { + code: `
Single line dups + no head/tail spaces
`, + output: `
Single line dups + no head/tail spaces
`, + errors: [{ messageId: 'invalidClassOrder' }], + }, + { + code: `
Single dups line + head spaces
`, + output: `
Single dups line + head spaces
`, + errors: [{ messageId: 'invalidClassOrder' }], + }, + { + code: `
Single line dups + tail spaces
`, + output: `
Single line dups + tail spaces
`, + errors: [{ messageId: 'invalidClassOrder' }], + }, + { + // Multiline + both head/tail spaces + code: ` + ctl(\` + hide + w:6@sm + block + hide + flex + block + w:12 + flex + block + w:4@lg + w:4@lg + \`);`, + output: ` + ctl(\` + block + flex + hide + w:12 + w:6@sm + w:4@lg + \`);`, + errors: [{ messageId: 'invalidClassOrder' }], + }, + { + code: ` + ctl(\` + invalid + w:6@sm + container + w:12 + flex + w:4@lg + \`);`, + output: ` + ctl(\` + invalid + container + flex + w:12 + w:6@sm + w:4@lg + \`);`, + errors: [{ messageId: 'invalidClassOrder' }], + }, + { + code: ` + const buttonClasses = ctl(\` + \${fullWidth ? "w:12" : "w:6"} + container + \${fullWidth ? "w:8@sm" : "w:4@sm"} + w:9@lg + flex + \${hasError && "bg:red"} + \`);`, + output: ` + const buttonClasses = ctl(\` + \${fullWidth ? "w:12" : "w:6"} + container + \${fullWidth ? "w:8@sm" : "w:4@sm"} + flex + w:9@lg + \${hasError && "bg:red"} + \`);`, + errors: [{ messageId: 'invalidClassOrder' }], + }, + { + code: ` + const buttonClasses = ctl(\` + \${fullWidth ? "w:12" : "w:6"} + flex + container + \${fullWidth ? "w:7@sm" : "w:4@sm"} + py:4@lg + py:6@sm + \${hasError && "bg:red"} + \`);`, + output: ` + const buttonClasses = ctl(\` + \${fullWidth ? "w:12" : "w:6"} + container + flex + \${fullWidth ? "w:7@sm" : "w:4@sm"} + py:6@sm + py:4@lg + \${hasError && "bg:red"} + \`);`, + errors: [{ messageId: 'invalidClassOrder' }, { messageId: 'invalidClassOrder' }], + }, + { + code: `
Allowed arbitrary value but incorrect order
`, + output: `
Allowed arbitrary value but incorrect order
`, + errors: [{ messageId: 'invalidClassOrder' }], + }, + { + code: `clsx(\`abs bottom:0 w:full h:70px flex flex:col\`);`, + output: `clsx(\`abs flex bottom:0 flex:col h:70px w:full\`);`, + options: [ + { + callees: ['clsx'], + }, + ], + errors: [{ messageId: 'invalidClassOrder' }], + }, + { + code: `cva({ + primary: ["abs bottom:0 w:full h:70px flex flex:col"], + })`, + output: `cva({ + primary: ["abs flex bottom:0 flex:col h:70px w:full"], + })`, + options: [ + { + callees: ['cva'], + }, + ], + errors: [{ messageId: 'invalidClassOrder' }], + }, + { + code: `
clsx
`, + output: `
clsx
`, + options: [ + { + callees: ['clsx'], + }, + ], + errors: [{ messageId: 'invalidClassOrder' }], + }, + { + code: ` + ctl(\` + px:2 + flex + \${ + !isDisabled && + \` + top:0 + flex + b:0 + \` + } + \${ + isDisabled && + \` + mx:0 + b:0 + \` + } + \`) + `, + output: ` + ctl(\` + flex + px:2 + \${ + !isDisabled && + \` + flex + b:0 + top:0 + \` } + \${ + isDisabled && + \` + b:0 + mx:0 + \` + } + \`) + `, + errors: [ + { messageId: 'invalidClassOrder' }, + { messageId: 'invalidClassOrder' }, + { messageId: 'invalidClassOrder' } + ], + }, + { + code: `
...
`, + output: `
...
`, + errors: [{ messageId: 'invalidClassOrder' }], + }, + { + code: `ctl(\`\${enabled && "px:2 flex"}\`)`, + output: `ctl(\`\${enabled && "flex px:2"}\`)`, + errors: [{ messageId: 'invalidClassOrder' }], + }, + { + code: `ctl(\`px:2 flex\`)`, + output: `ctl(\`flex px:2\`)`, + errors: [{ messageId: 'invalidClassOrder' }], + }, + { + code: ` + ctl(\` + px:2 + flex + \`) + `, + output: ` + ctl(\` + flex + px:2 + \`) + `, + errors: [{ messageId: 'invalidClassOrder' }], + }, + { + code: ` +
+ `, + output: ` +
+ `, + errors: [{ messageId: 'invalidClassOrder' }, { messageId: 'invalidClassOrder' }], + }, + { + code: ` + classnames([ + 'invalid w:4@lg w:6@sm', + ['w:12 flex'], + ])`, + output: ` + classnames([ + 'invalid w:6@sm w:4@lg', + ['flex w:12'], + ])`, + errors: [{ messageId: 'invalidClassOrder' }, { messageId: 'invalidClassOrder' }], + }, + { + code: ` + classnames({ + invalid, + flex: myFlag, + 'w:4@lg w:6@sm': resize + })`, + output: ` + classnames({ + invalid, + flex: myFlag, + 'w:6@sm w:4@lg': resize + })`, + errors: [{ messageId: 'invalidClassOrder' }], + }, + { + code: `ctl(\`\${some} container animate-spin first:flex \${bool ? "flex:col flex" : ""}\`)`, + output: `ctl(\`\${some} container animate-spin first:flex \${bool ? "flex flex:col" : ""}\`)`, + errors: [{ messageId: 'invalidClassOrder' }], + }, + { + code: `ctl(\`p:3 b:3|solid|gray m:4 h:24 p:4@lg flex b:2 m:4@lg\`)`, + output: `ctl(\`flex b:3|solid|gray b:2 h:24 m:4 m:4@lg p:3 p:4@lg\`)`, + errors: [{ messageId: 'invalidClassOrder' }], + }, ], }) \ No newline at end of file