diff --git a/packages/shared-integration/src/sort-rules.ts b/packages/shared-integration/src/sort-rules.ts new file mode 100644 index 0000000000..b75e138e59 --- /dev/null +++ b/packages/shared-integration/src/sort-rules.ts @@ -0,0 +1,36 @@ +import type { UnoGenerator } from '@unocss/core' +import { notNull } from '@unocss/core' + +export async function sortRules(rules: string[], uno: UnoGenerator) { + const unknown: string[] = [] + + // enable details for variant handlers + uno.config.details = true + + const result = await Promise.all(rules + .map(async (i) => { + const token = await uno.parseToken(i) + if (token == null) { + unknown.push(i) + return undefined + } + const variantRank = (token[0][5]?.variantHandlers?.length || 0) * 1000 + const order = token[0][0] + variantRank + return [order, i] as const + })) + + const sorted = result + .filter(notNull) + .sort((a, b) => { + let result = a[0] - b[0] + if (result === 0) + result = a[1].localeCompare(b[1]) + return result + }) + .map(i => i[1]) + + return { + sorted, + unknown, + } +} diff --git a/test/sort-rules.test.ts b/test/sort-rules.test.ts new file mode 100644 index 0000000000..faeea7e136 --- /dev/null +++ b/test/sort-rules.test.ts @@ -0,0 +1,27 @@ +import { createGenerator } from '@unocss/core' +import presetUno from '@unocss/preset-uno' +import { describe, expect, test } from 'vitest' +import { sortRules } from '../packages/shared-integration/src/sort-rules' + +describe('sort rules', () => { + const uno = createGenerator({ + presets: [ + presetUno(), + ], + }) + + async function sort(tokens: string) { + const result = await sortRules(tokens.split(' '), uno) + return [...result.sorted, ...result.unknown].join(' ') + } + test('basic', async () => { + expect(await sort('pt-2 p-4 foo')) + .toMatchInlineSnapshot('"p-4 pt-2 foo"') + expect(await sort('hover:focus:p1 hover:mt1 hover:m2 pt-2 p-4')) + .toMatchInlineSnapshot('"p-4 pt-2 hover:m2 hover:mt1 hover:focus:p1"') + expect(await sort('hover:opacity-75 opacity-50 hover:scale-150 scale-125')) + .toMatchInlineSnapshot('"scale-125 opacity-50 hover:scale-150 hover:opacity-75"') + expect(await sort('lg:grid-cols-4 grid sm:grid-cols-3 grid-cols-2')) + .toMatchInlineSnapshot('"grid grid-cols-2 lg:grid-cols-4 sm:grid-cols-3"') + }) +})