eslint-plugin-better-tailwindcss
Version:
auto-wraps tailwind classes after a certain print width or class count into multiple lines to improve readability.
76 lines • 3.41 kB
JavaScript
import { array, boolean, description, optional, pipe, strictObject, string } from "valibot";
import { createGetDissectedClasses, getDissectedClasses } from "../tailwindcss/dissect-classes.js";
import { async } from "../utils/context.js";
import { lintClasses } from "../utils/lint.js";
import { getCachedRegex } from "../async-utils/regex.js";
import { createRule } from "../utils/rule.js";
import { splitClasses } from "../utils/utils.js";
export const enforceMotionSafeVariant = createRule({
autofix: false,
category: "correctness",
description: "Enforce the 'motion-safe' variant for transition and animation classes.",
docs: "https://github.com/schoero/eslint-plugin-better-tailwindcss/blob/main/docs/rules/enforce-motion-safe-variant.md",
name: "enforce-motion-safe-variant",
recommended: false,
messages: {
missing: "{{ className }} is missing the 'motion-safe' variant."
},
schema: strictObject({
classes: optional(pipe(array(string()), description("A list of regular expression patterns for classes that require the 'motion-safe' variant.")), ["^transition(-.*)?$", "^animate(-.*)?$"]),
motionReduce: optional(pipe(boolean(), description("Suppress warnings for a literal that already contains a class with the 'motion-reduce' variant (in itself or in prior literals).")), false)
}),
initialize: ctx => {
createGetDissectedClasses(ctx);
},
lintLiterals: (ctx, literals) => lintLiterals(ctx, literals)
});
function lintLiterals(ctx, literals) {
const { classes: classPatterns, motionReduce } = ctx.options;
for (const literal of literals) {
const classes = splitClasses(literal.content);
const { dissectedClasses, warnings } = getDissectedClasses(async(ctx), classes);
if (motionReduce && hasMotionReduceVariant(ctx, literal, dissectedClasses)) {
continue;
}
lintClasses(ctx, literal, className => {
const dissectedClass = dissectedClasses[className];
if (dissectedClass?.variants === undefined) {
return;
}
if (!classPatterns.some(pattern => getCachedRegex(pattern).test(dissectedClass.base))) {
return;
}
if (dissectedClass.variants.includes("motion-safe") || dissectedClass.variants.includes("motion-reduce")) {
return;
}
return {
data: { className },
id: "missing",
warnings
};
});
}
}
function hasMotionReduceVariant(ctx, literal, dissectedClasses) {
if (hasMotionReduceInClasses(dissectedClasses)) {
return true;
}
if (!literal.priorLiterals) {
return false;
}
for (const priorLiteral of literal.priorLiterals) {
if (!priorLiteral) {
continue;
}
const priorClasses = splitClasses(priorLiteral.content);
const { dissectedClasses: priorDissectedClasses } = getDissectedClasses(async(ctx), priorClasses);
if (hasMotionReduceInClasses(priorDissectedClasses)) {
return true;
}
}
return false;
}
function hasMotionReduceInClasses(dissectedClasses) {
return Object.values(dissectedClasses).some(dissectedClass => dissectedClass.variants?.includes("motion-reduce"));
}
//# sourceMappingURL=enforce-motion-safe-variant.js.map