diff --git a/docs/rules/classnames-order.md b/docs/rules/classnames-order.md index 7b7baf82..ef20c150 100644 --- a/docs/rules/classnames-order.md +++ b/docs/rules/classnames-order.md @@ -25,6 +25,7 @@ Examples of **correct** code for this rule: "tailwindcss/classnames-order": [, { "callees": Array, "config": |, + "groupByResponsive": , "groups": Array, "prependCustom": , "removeDuplicates": @@ -52,6 +53,20 @@ It is also possible to directly inject a configuration as plain `object` like `{ Finally, the plugin will [merge the provided configuration](https://tailwindcss.com/docs/configuration#referencing-in-java-script) with [Tailwind CSS's default configuration](https://github.com/tailwindlabs/tailwindcss/blob/master/stubs/defaultConfig.stub.js). +### `groupByResponsive` (default: `false`) + +Linting this code: + +`
...
` + +By default, the ordering process will group the classnames by properties, then by variants: + +`
...
` + +Set `groupByResponsive` to `true` and the ordering and you will get: + +`
...
` + ### `groups` (default defined in [groups.js](../../lib/config/groups.js)) If you really need to, you can write your own configuration. diff --git a/lib/rules/classnames-order.js b/lib/rules/classnames-order.js index 36bed074..db4b6369 100644 --- a/lib/rules/classnames-order.js +++ b/lib/rules/classnames-order.js @@ -67,6 +67,7 @@ module.exports = { const callees = getOption(context, 'callees'); const twConfig = getOption(context, 'config'); const groupsConfig = getOption(context, 'groups'); + const groupByResponsive = getOption(context, 'groupByResponsive'); const prependCustom = getOption(context, 'prependCustom'); const removeDuplicates = getOption(context, 'removeDuplicates'); @@ -106,6 +107,23 @@ module.exports = { } return str; }; + /** + * Generate an Array of Array, grouping class by responsive variants + * @param {Array} classNames + * @returns {Array} an Array (one entry per responsive variant), each entry is an array of classnames + */ + const getResponsiveGroups = (classNames) => { + const responsiveVariants = Object.keys(mergedConfig.theme.screens); + const classnamesByResponsive = [[]]; + responsiveVariants.forEach((prefix) => { + classnamesByResponsive.push([]); + }); + classNames.forEach((cls) => { + const idx = parseInt(getSpecificity(attrUtil.cleanClassname(cls), responsiveVariants, true), 10); + classnamesByResponsive[idx].push(cls); + }); + return classnamesByResponsive; + }; /** * Parse each classname and populate the `sorted` and `extra` arrays @@ -259,11 +277,24 @@ module.exports = { } // Sorting - const { sorted, extras } = getSortedGroups(classNames); + const mergedSorted = []; + const mergedExtras = []; + if (groupByResponsive) { + const respGroups = getResponsiveGroups(classNames); + respGroups.forEach((clsGroup) => { + const { sorted, extras } = getSortedGroups(clsGroup); + mergedSorted.push(...sorted); + mergedExtras.push(...extras); + }); + } else { + const { sorted, extras } = getSortedGroups(classNames); + mergedSorted.push(...sorted); + mergedExtras.push(...extras); + } // Generates the validated/sorted attribute value - const flatted = sorted.flat(); - const union = prependCustom ? [...extras, ...flatted] : [...flatted, ...extras]; + const flatted = mergedSorted.flat(); + const union = prependCustom ? [...mergedExtras, ...flatted] : [...flatted, ...mergedExtras]; if (before !== null) { union.unshift(before); } diff --git a/lib/util/settings.js b/lib/util/settings.js index 0613d579..0efe5bc0 100644 --- a/lib/util/settings.js +++ b/lib/util/settings.js @@ -19,6 +19,8 @@ function getOption(context, name) { return 'tailwind.config.js'; case 'cssFiles': return ['**/*.css', '!**/node_modules']; + case 'groupByResponsive': + return false; case 'groups': return defaultGroups; case 'prependCustom': diff --git a/tests/lib/rules/classnames-order.js b/tests/lib/rules/classnames-order.js index cc4aa247..4305dc75 100644 --- a/tests/lib/rules/classnames-order.js +++ b/tests/lib/rules/classnames-order.js @@ -39,6 +39,14 @@ ruleTester.run("classnames-order", rule, { { code: `
Simple quotes
`, }, + { + code: `
groupByResponsive
`, + options: [ + { + groupByResponsive: true, + }, + ], + }, { code: `
'p', then 'py' then 'px'
`, },