From e0bd09a2462a3917164c63e69091ac620b777202 Mon Sep 17 00:00:00 2001 From: Maarten Sijm <9739541+mpsijm@users.noreply.github.com> Date: Tue, 7 Jun 2022 15:41:12 +0200 Subject: [PATCH 1/3] Fix #127: Don't ignore removeDuplicates option when using officialSorting The check for removing duplicates is completely separate from the sorting, even when {officialSorting: false}. Therefore, it shouldn't harm to also execute this check when {officialSorting: true}. --- docs/rules/classnames-order.md | 2 +- lib/rules/classnames-order.js | 8 ++++---- tests/lib/rules/classnames-order.js | 10 ++++++++++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/docs/rules/classnames-order.md b/docs/rules/classnames-order.md index ffbfeb0e..2dfe3fe0 100644 --- a/docs/rules/classnames-order.md +++ b/docs/rules/classnames-order.md @@ -118,7 +118,7 @@ const customGroups = require('custom-groups').groups; ### `officialSorting` (default: `false`) -Set `officialSorting` to `true` if you want to use the same ordering rules as the official plugin `prettier-plugin-tailwindcss`. Enabling this settings will cause `groupByResponsive`, `groups`, `prependCustom` and `removeDuplicates` options to be ignored. +Set `officialSorting` to `true` if you want to use the same ordering rules as the official plugin `prettier-plugin-tailwindcss`. Enabling this setting will cause `groupByResponsive`, `groups`, and `prependCustom` options to be ignored. ### `prependCustom` (default: `false`) diff --git a/lib/rules/classnames-order.js b/lib/rules/classnames-order.js index 96307043..b8c7c388 100644 --- a/lib/rules/classnames-order.js +++ b/lib/rules/classnames-order.js @@ -309,6 +309,10 @@ module.exports = { return; } + if (removeDuplicates) { + removeDuplicatesFromClassnamesAndWhitespaces(classNames, whitespaces, headSpace, tailSpace); + } + let orderedClassNames; let validatedClassNamesValue = ''; @@ -323,10 +327,6 @@ module.exports = { } } } else { - if (removeDuplicates) { - removeDuplicatesFromClassnamesAndWhitespaces(classNames, whitespaces, headSpace, tailSpace); - } - // Sorting const mergedSorted = []; const mergedExtras = []; diff --git a/tests/lib/rules/classnames-order.js b/tests/lib/rules/classnames-order.js index 130c5366..df79d70b 100644 --- a/tests/lib/rules/classnames-order.js +++ b/tests/lib/rules/classnames-order.js @@ -817,6 +817,16 @@ ruleTester.run("classnames-order", rule, { }, ], }, + { + code: `
Using official sorting, with duplicates
`, + output: `
Using official sorting, with duplicates
`, + errors: errors, + options: [ + { + officialSorting: true, + }, + ], + }, { code: `ctl(\`\${some} container animate-spin first:flex \${bool ? "flex-col flex" : ""}\`)`, output: `ctl(\`\${some} container animate-spin first:flex \${bool ? "flex flex-col" : ""}\`)`, From 9a979647b2abf3256ab10008ede167e9b1035e1c Mon Sep 17 00:00:00 2001 From: Maarten Sijm <9739541+mpsijm@users.noreply.github.com> Date: Tue, 7 Jun 2022 15:53:06 +0200 Subject: [PATCH 2/3] Extract duplicate code that converts ordered classNames back to attribute value --- lib/rules/classnames-order.js | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/lib/rules/classnames-order.js b/lib/rules/classnames-order.js index b8c7c388..bae16b39 100644 --- a/lib/rules/classnames-order.js +++ b/lib/rules/classnames-order.js @@ -314,20 +314,11 @@ module.exports = { } let orderedClassNames; - let validatedClassNamesValue = ''; + // Sorting if (officialSorting) { orderedClassNames = order(classNames, contextFallback); - for (let i = 0; i < orderedClassNames.length; i++) { - 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] ?? ''; - } - } } else { - // Sorting const mergedSorted = []; const mergedExtras = []; if (groupByResponsive) { @@ -343,18 +334,21 @@ module.exports = { mergedExtras.push(...extras); } - // Generates the validated/sorted attribute value const flatted = mergedSorted.flat(); - const union = prependCustom ? [...mergedExtras, ...flatted] : [...flatted, ...mergedExtras]; - for (let i = 0; i < union.length; i++) { - const w = whitespaces[i] ?? ''; - const cls = union[i]; - validatedClassNamesValue += headSpace ? `${w}${cls}` : `${cls}${w}`; - if (headSpace && tailSpace && i === union.length - 1) { - validatedClassNamesValue += whitespaces[whitespaces.length - 1] ?? ''; - } + orderedClassNames = prependCustom ? [...mergedExtras, ...flatted] : [...flatted, ...mergedExtras]; + } + + // Generates the validated/sorted attribute value + let validatedClassNamesValue = ''; + for (let i = 0; i < orderedClassNames.length; i++) { + 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 (originalClassNamesValue !== validatedClassNamesValue) { validatedClassNamesValue = prefix + validatedClassNamesValue + suffix; context.report({ From 3954659b8f7bd02fd6a430fe91eb3f75f602ad5c Mon Sep 17 00:00:00 2001 From: Maarten Sijm <9739541+mpsijm@users.noreply.github.com> Date: Tue, 7 Jun 2022 16:08:31 +0200 Subject: [PATCH 3/3] #136: Remove duplicates from ordered classNames instead of unordered classNames --- lib/rules/classnames-order.js | 8 +++---- ...eDuplicatesFromClassnamesAndWhitespaces.js | 24 +++++++++---------- tests/lib/rules/classnames-order.js | 6 ++--- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/lib/rules/classnames-order.js b/lib/rules/classnames-order.js index bae16b39..051936c4 100644 --- a/lib/rules/classnames-order.js +++ b/lib/rules/classnames-order.js @@ -309,10 +309,6 @@ module.exports = { return; } - if (removeDuplicates) { - removeDuplicatesFromClassnamesAndWhitespaces(classNames, whitespaces, headSpace, tailSpace); - } - let orderedClassNames; // Sorting @@ -338,6 +334,10 @@ module.exports = { orderedClassNames = prependCustom ? [...mergedExtras, ...flatted] : [...flatted, ...mergedExtras]; } + if (removeDuplicates) { + removeDuplicatesFromClassnamesAndWhitespaces(orderedClassNames, whitespaces, headSpace, tailSpace); + } + // Generates the validated/sorted attribute value let validatedClassNamesValue = ''; for (let i = 0; i < orderedClassNames.length; i++) { diff --git a/lib/util/removeDuplicatesFromClassnamesAndWhitespaces.js b/lib/util/removeDuplicatesFromClassnamesAndWhitespaces.js index 1b1ea51f..f660273c 100644 --- a/lib/util/removeDuplicatesFromClassnamesAndWhitespaces.js +++ b/lib/util/removeDuplicatesFromClassnamesAndWhitespaces.js @@ -1,20 +1,18 @@ 'use strict'; -function removeDuplicatesFromClassnamesAndWhitespaces(classNames, whitespaces, headSpace, tailSpace) { - const uniqueSet = new Set(classNames); - if (uniqueSet.size === classNames.length) { - return; - } +function removeDuplicatesFromClassnamesAndWhitespaces(orderedClassNames, whitespaces, headSpace, tailSpace) { + let previous = orderedClassNames[0]; const offset = (!headSpace && !tailSpace) || tailSpace ? -1 : 0; - uniqueSet.forEach((cls) => { - let duplicatedInstances = classNames.filter((el) => el === cls).length - 1; - while (duplicatedInstances > 0) { - const idx = classNames.lastIndexOf(cls); - classNames.splice(idx, 1); - whitespaces.splice(idx + offset, 1); - duplicatedInstances--; + for (let i = 1; i < orderedClassNames.length; i++) { + const cls = orderedClassNames[i]; + // This function assumes that the list of classNames is ordered, so just comparing to the previous className is enough + if (cls === previous) { + orderedClassNames.splice(i, 1); + whitespaces.splice(i + offset, 1); + i--; } - }); + previous = cls; + } } module.exports = removeDuplicatesFromClassnamesAndWhitespaces; diff --git a/tests/lib/rules/classnames-order.js b/tests/lib/rules/classnames-order.js index df79d70b..c67ea0fc 100644 --- a/tests/lib/rules/classnames-order.js +++ b/tests/lib/rules/classnames-order.js @@ -382,17 +382,17 @@ ruleTester.run("classnames-order", rule, { }, { code: `
Single line dups + no head/tail spaces
`, - output: `
Single line dups + no head/tail spaces
`, + output: `
Single line dups + no head/tail spaces
`, errors: errors, }, { code: `
Single dups line + head spaces
`, - output: `
Single dups line + head spaces
`, + output: `
Single dups line + head spaces
`, errors: errors, }, { code: `
Single line dups + tail spaces
`, - output: `
Single line dups + tail spaces
`, + output: `
Single line dups + tail spaces
`, errors: errors, }, {