diff --git a/src/rules/enforce-consistent-class-order.test.ts b/src/rules/enforce-consistent-class-order.test.ts
index fd99153..0ab3518 100644
--- a/src/rules/enforce-consistent-class-order.test.ts
+++ b/src/rules/enforce-consistent-class-order.test.ts
@@ -92,6 +92,46 @@ describe(enforceConsistentClassOrder.name, () => {
);
});
+ it("should sort alphabetically in a locale-independent way for `asc` and `desc`", () => {
+ lint(
+ enforceConsistentClassOrder,
+ {
+ invalid: [
+ {
+ angular: `
`,
+ angularOutput: `
`,
+ html: `
`,
+ htmlOutput: `
`,
+ jsx: `() =>
`,
+ jsxOutput: `() =>
`,
+ svelte: `
`,
+ svelteOutput: `
`,
+ vue: `
`,
+ vueOutput: `
`,
+
+ errors: 1,
+ options: [{ order: "asc" }]
+ },
+ {
+ angular: `
`,
+ angularOutput: `
`,
+ html: `
`,
+ htmlOutput: `
`,
+ jsx: `() =>
`,
+ jsxOutput: `() =>
`,
+ svelte: `
`,
+ svelteOutput: `
`,
+ vue: `
`,
+ vueOutput: `
`,
+
+ errors: 1,
+ options: [{ order: "desc" }]
+ }
+ ]
+ }
+ );
+ });
+
it("should group all classes with the same variant together", () => {
lint(enforceConsistentClassOrder, {
invalid: [
@@ -591,6 +631,46 @@ describe(enforceConsistentClassOrder.name, () => {
);
});
+ it("should sort unknown classes alphabetically in a locale-independent way", () => {
+ lint(
+ enforceConsistentClassOrder,
+ {
+ invalid: [
+ {
+ angular: `
`,
+ angularOutput: `
`,
+ html: `
`,
+ htmlOutput: `
`,
+ jsx: `() =>
`,
+ jsxOutput: `() =>
`,
+ svelte: `
`,
+ svelteOutput: `
`,
+ vue: `
`,
+ vueOutput: `
`,
+
+ errors: 1,
+ options: [{ order: "official", unknownClassOrder: "asc", unknownClassPosition: "start" }]
+ },
+ {
+ angular: `
`,
+ angularOutput: `
`,
+ html: `
`,
+ htmlOutput: `
`,
+ jsx: `() =>
`,
+ jsxOutput: `() =>
`,
+ svelte: `
`,
+ svelteOutput: `
`,
+ vue: `
`,
+ vueOutput: `
`,
+
+ 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,
@@ -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: `
`,
+ angularOutput: `
`,
+ html: `
`,
+ htmlOutput: `
`,
+ jsx: `() =>
`,
+ jsxOutput: `() =>
`,
+ svelte: `
`,
+ svelteOutput: `
`,
+ vue: `
`,
+ vueOutput: `
`,
+
+ 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: `
`,
+ angularOutput: `
`,
+ html: `
`,
+ htmlOutput: `
`,
+ jsx: `() =>
`,
+ jsxOutput: `() =>
`,
+ svelte: `
`,
+ svelteOutput: `
`,
+ vue: `
`,
+ vueOutput: `
`,
+
+ 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,
diff --git a/src/rules/enforce-consistent-class-order.ts b/src/rules/enforce-consistent-class-order.ts
index 79b3246..a78c00c 100644
--- a/src/rules/enforce-consistent-class-order.ts
+++ b/src/rules/enforce-consistent-class-order.ts
@@ -162,11 +162,11 @@ function sortClassNames(ctx: Context, 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);
@@ -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;
}
@@ -333,10 +333,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;
}
@@ -344,6 +344,11 @@ function getCustomOrder(position: "end" | "start", order: "asc" | "desc" | "pres
}
+function compareClasses(classA: string, classB: string): number {
+ if(classA === classB){ return 0; }
+ return classA < classB ? -1 : +1;
+}
+
function isArbitrary(variant?: string): boolean {
if(!variant){
return false;