Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 154 additions & 0 deletions src/rules/enforce-consistent-class-order.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,46 @@ describe(enforceConsistentClassOrder.name, () => {
);
});

it("should sort alphabetically in a locale-independent way for `asc` and `desc`", () => {
lint(
enforceConsistentClassOrder,
{
invalid: [
{
angular: `<img class="py-24 px-12" />`,
angularOutput: `<img class="px-12 py-24" />`,
html: `<img class="py-24 px-12" />`,
htmlOutput: `<img class="px-12 py-24" />`,
jsx: `() => <img class="py-24 px-12" />`,
jsxOutput: `() => <img class="px-12 py-24" />`,
svelte: `<img class="py-24 px-12" />`,
svelteOutput: `<img class="px-12 py-24" />`,
vue: `<template><img class="py-24 px-12" /></template>`,
vueOutput: `<template><img class="px-12 py-24" /></template>`,

errors: 1,
options: [{ order: "asc" }]
},
{
angular: `<img class="px-12 py-24" />`,
angularOutput: `<img class="py-24 px-12" />`,
html: `<img class="px-12 py-24" />`,
htmlOutput: `<img class="py-24 px-12" />`,
jsx: `() => <img class="px-12 py-24" />`,
jsxOutput: `() => <img class="py-24 px-12" />`,
svelte: `<img class="px-12 py-24" />`,
svelteOutput: `<img class="py-24 px-12" />`,
vue: `<template><img class="px-12 py-24" /></template>`,
vueOutput: `<template><img class="py-24 px-12" /></template>`,

errors: 1,
options: [{ order: "desc" }]
}
]
}
);
Comment thread
schoero marked this conversation as resolved.
});

it("should group all classes with the same variant together", () => {
lint(enforceConsistentClassOrder, {
invalid: [
Expand Down Expand Up @@ -591,6 +631,46 @@ describe(enforceConsistentClassOrder.name, () => {
);
});

it("should sort unknown classes alphabetically in a locale-independent way", () => {
lint(
enforceConsistentClassOrder,
{
invalid: [
{
angular: `<img class="flex cy-24 cx-12" />`,
angularOutput: `<img class="cx-12 cy-24 flex" />`,
html: `<img class="flex cy-24 cx-12" />`,
htmlOutput: `<img class="cx-12 cy-24 flex" />`,
jsx: `() => <img class="flex cy-24 cx-12" />`,
jsxOutput: `() => <img class="cx-12 cy-24 flex" />`,
svelte: `<img class="flex cy-24 cx-12" />`,
svelteOutput: `<img class="cx-12 cy-24 flex" />`,
vue: `<template><img class="flex cy-24 cx-12" /></template>`,
vueOutput: `<template><img class="cx-12 cy-24 flex" /></template>`,

errors: 1,
options: [{ order: "official", unknownClassOrder: "asc", unknownClassPosition: "start" }]
},
{
angular: `<img class="flex cx-12 cy-24" />`,
angularOutput: `<img class="flex cy-24 cx-12" />`,
html: `<img class="flex cx-12 cy-24" />`,
htmlOutput: `<img class="flex cy-24 cx-12" />`,
jsx: `() => <img class="flex cx-12 cy-24" />`,
jsxOutput: `() => <img class="flex cy-24 cx-12" />`,
svelte: `<img class="flex cx-12 cy-24" />`,
svelteOutput: `<img class="flex cy-24 cx-12" />`,
vue: `<template><img class="flex cx-12 cy-24" /></template>`,
vueOutput: `<template><img class="flex cy-24 cx-12" /></template>`,

errors: 1,
options: [{ order: "official", unknownClassOrder: "desc", unknownClassPosition: "end" }]
}
]
}
);
});

it.runIf(getTailwindCSSVersion().major >= 4)("should sort component classes to the start by default in tailwind >= 4", () => {
lint(
enforceConsistentClassOrder,
Expand Down Expand Up @@ -631,6 +711,80 @@ describe(enforceConsistentClassOrder.name, () => {
);
});

it.runIf(getTailwindCSSVersion().major >= 4)("should sort component classes alphabetically in a locale-independent way in tailwind >= 4", () => {
lint(
enforceConsistentClassOrder,
{
invalid: [
{
angular: `<img class="flex cy-24 cx-12" />`,
angularOutput: `<img class="cx-12 cy-24 flex" />`,
html: `<img class="flex cy-24 cx-12" />`,
htmlOutput: `<img class="cx-12 cy-24 flex" />`,
jsx: `() => <img class="flex cy-24 cx-12" />`,
jsxOutput: `() => <img class="cx-12 cy-24 flex" />`,
svelte: `<img class="flex cy-24 cx-12" />`,
svelteOutput: `<img class="cx-12 cy-24 flex" />`,
vue: `<template><img class="flex cy-24 cx-12" /></template>`,
vueOutput: `<template><img class="cx-12 cy-24 flex" /></template>`,

errors: 1,

files: {
"tailwind.css": css`
@import "tailwindcss";

@layer components {
.cx-12, .cy-24 {
@apply font-bold;
}
}
`
},
options: [{
componentClassOrder: "asc",
componentClassPosition: "start",
detectComponentClasses: true,
entryPoint: "./tailwind.css"
}]
},
{
angular: `<img class="flex cx-12 cy-24" />`,
angularOutput: `<img class="flex cy-24 cx-12" />`,
html: `<img class="flex cx-12 cy-24" />`,
htmlOutput: `<img class="flex cy-24 cx-12" />`,
jsx: `() => <img class="flex cx-12 cy-24" />`,
jsxOutput: `() => <img class="flex cy-24 cx-12" />`,
svelte: `<img class="flex cx-12 cy-24" />`,
svelteOutput: `<img class="flex cy-24 cx-12" />`,
vue: `<template><img class="flex cx-12 cy-24" /></template>`,
vueOutput: `<template><img class="flex cy-24 cx-12" /></template>`,

errors: 1,

files: {
"tailwind.css": css`
@import "tailwindcss";

@layer components {
.cx-12, .cy-24 {
@apply font-bold;
}
}
`
},
options: [{
componentClassOrder: "desc",
componentClassPosition: "end",
detectComponentClasses: true,
entryPoint: "./tailwind.css"
}]
}
]
}
);
});

it.runIf(getTailwindCSSVersion().major >= 4)("should differentiate between custom component classes and unknown classes in tailwind >= 4", () => {
lint(
enforceConsistentClassOrder,
Expand Down
17 changes: 11 additions & 6 deletions src/rules/enforce-consistent-class-order.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,11 @@ function sortClassNames(ctx: Context<typeof enforceConsistentClassOrder>, classe
const { componentClassOrder, componentClassPosition, order, unknownClassOrder, unknownClassPosition } = ctx.options;

if(order === "asc"){
return [classes.toSorted((a, b) => a.localeCompare(b))];
return [classes.toSorted((a, b) => compareClasses(a, b))];
}

if(order === "desc"){
return [classes.toSorted((a, b) => b.localeCompare(a))];
return [classes.toSorted((a, b) => compareClasses(b, a))];
}

const { classOrder, warnings } = getClassOrder(async(ctx), classes);
Expand Down Expand Up @@ -319,10 +319,10 @@ function getCustomOrder(position: "end" | "start", order: "asc" | "desc" | "pres

if(aIsCustomClass && bIsCustomClass){
if(order === "asc"){
return classA.localeCompare(classB);
return compareClasses(classA, classB);
}
if(order === "desc"){
return classB.localeCompare(classA);
return compareClasses(classB, classA);
}
return 0;
}
Expand All @@ -333,17 +333,22 @@ function getCustomOrder(position: "end" | "start", order: "asc" | "desc" | "pres

if(aIsCustomClass && bIsCustomClass){
if(order === "asc"){
return classA.localeCompare(classB);
return compareClasses(classA, classB);
}
if(order === "desc"){
return classB.localeCompare(classA);
return compareClasses(classB, classA);
}
return 0;
}
}

}

function compareClasses(classA: string, classB: string): number {
if(classA === classB){ return 0; }
return classA < classB ? -1 : +1;
}
Comment thread
schoero marked this conversation as resolved.

function isArbitrary(variant?: string): boolean {
if(!variant){
return false;
Expand Down