From 63537aaa89ed7430f544f86b0efbb07d4e2f67be Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Wed, 16 Feb 2022 11:03:00 +0100 Subject: [PATCH] Implement `getClassOrder` instead of `sortClassList` (#7459) * implement `getSortOrder` instead of `sortClassList` * rename `getSortOrder` to `getClassOrder` * update changelog --- CHANGELOG.md | 1 + src/lib/setupContextUtils.js | 30 +++++--------- ...ClassList.test.js => getSortOrder.test.js} | 41 ++++++++++++++++++- 3 files changed, 51 insertions(+), 21 deletions(-) rename tests/{sortClassList.test.js => getSortOrder.test.js} (64%) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2b40ae4f703..b2bd17b56d5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Remove opacity variables from `:visited` pseudo class ([#7458](https://github.com/tailwindlabs/tailwindcss/pull/7458)) - Support arbitrary values + calc + theme with quotes ([#7462](https://github.com/tailwindlabs/tailwindcss/pull/7462)) - Don't duplicate layer output when scanning content with variants + wildcards ([#7478](https://github.com/tailwindlabs/tailwindcss/pull/7478)) +- Implement `getClassOrder` instead of `sortClassList` ([#7459](https://github.com/tailwindlabs/tailwindcss/pull/7459)) ## [3.0.22] - 2022-02-11 diff --git a/src/lib/setupContextUtils.js b/src/lib/setupContextUtils.js index 94128c2d26da..c4740c46e113 100644 --- a/src/lib/setupContextUtils.js +++ b/src/lib/setupContextUtils.js @@ -744,33 +744,25 @@ function registerPlugins(plugins, context) { // sorting could be weird since you still require them in order to make the // host utitlies work properly. (Thanks Biology) let parasiteUtilities = new Set([prefix(context, 'group'), prefix(context, 'peer')]) - context.sortClassList = function sortClassList(classes) { + context.getClassOrder = function getClassOrder(classes) { let sortedClassNames = new Map() for (let [sort, rule] of generateRules(new Set(classes), context)) { if (sortedClassNames.has(rule.raws.tailwind.candidate)) continue sortedClassNames.set(rule.raws.tailwind.candidate, sort) } - return classes - .map((className) => { - let order = sortedClassNames.get(className) ?? null + return classes.map((className) => { + let order = sortedClassNames.get(className) ?? null - if (order === null && parasiteUtilities.has(className)) { - // This will make sure that it is at the very beginning of the - // `components` layer which technically means 'before any - // components'. - order = context.layerOrder.components - } + if (order === null && parasiteUtilities.has(className)) { + // This will make sure that it is at the very beginning of the + // `components` layer which technically means 'before any + // components'. + order = context.layerOrder.components + } - return [className, order] - }) - .sort(([, a], [, z]) => { - if (a === z) return 0 - if (a === null) return -1 - if (z === null) return 1 - return bigSign(a - z) - }) - .map(([className]) => className) + return [className, order] + }) } // Generate a list of strings for autocompletion purposes, e.g. diff --git a/tests/sortClassList.test.js b/tests/getSortOrder.test.js similarity index 64% rename from tests/sortClassList.test.js rename to tests/getSortOrder.test.js index 0220f190727a..b036273e15e0 100644 --- a/tests/sortClassList.test.js +++ b/tests/getSortOrder.test.js @@ -1,5 +1,42 @@ import resolveConfig from '../src/public/resolve-config' import { createContext } from '../src/lib/setupContextUtils' +import bigSign from '../src/util/bigSign' + +/** + * This is a function that the prettier-plugin-tailwindcss would use. It would + * do the actual sorting based on the classes and order we return from `getClassOrder`. + * + * This way the actual sorting logic is done in the plugin which allows you to + * put unknown classes at the end for example. + * + * @param {Array<[string, bigint]>} arrayOfTuples + * @returns {string} + */ +function defaultSort(arrayOfTuples) { + return arrayOfTuples + .sort(([, a], [, z]) => { + if (a === z) return 0 + if (a === null) return -1 + if (z === null) return 1 + return bigSign(a - z) + }) + .map(([className]) => className) + .join(' ') +} + +it('should return a list of tuples with the sort order', () => { + let input = 'font-bold underline hover:font-medium unknown' + let config = {} + let context = createContext(resolveConfig(config)) + expect(context.getClassOrder(input.split(' '))).toEqual([ + ['font-bold', expect.any(BigInt)], + ['underline', expect.any(BigInt)], + ['hover:font-medium', expect.any(BigInt)], + + // Unknown values receive `null` + ['unknown', null], + ]) +}) it.each([ // Utitlies @@ -33,7 +70,7 @@ it.each([ ])('should sort "%s" based on the order we generate them in to "%s"', (input, output) => { let config = {} let context = createContext(resolveConfig(config)) - expect(context.sortClassList(input.split(' ')).join(' ')).toEqual(output) + expect(defaultSort(context.getClassOrder(input.split(' ')))).toEqual(output) }) it.each([ @@ -73,6 +110,6 @@ it.each([ (input, output) => { let config = { prefix: 'tw-' } let context = createContext(resolveConfig(config)) - expect(context.sortClassList(input.split(' ')).join(' ')).toEqual(output) + expect(defaultSort(context.getClassOrder(input.split(' ')))).toEqual(output) } )