eslint-plugin-readable-tailwind
Version:
auto-wraps tailwind classes after a certain print width or class count into multiple lines to improve readability.
106 lines • 4.43 kB
JavaScript
import { findDefaultConfig, findTailwindConfig } from "./config.js";
import { createTailwindContextFromEntryPoint } from "./context.js";
export async function getConflictingClasses({ classes, configPath, cwd }) {
const warnings = [];
const config = findTailwindConfig(cwd, configPath);
const defaultConfig = findDefaultConfig(cwd);
if (!config) {
warnings.push({
option: "entryPoint",
title: configPath
? `No tailwind css config found at \`${configPath}\``
: "No tailwind css entry point configured"
});
}
const path = config?.path ?? defaultConfig.path;
const invalidate = config?.invalidate ?? defaultConfig.invalidate;
if (!path) {
throw new Error("Could not find a valid Tailwind CSS configuration");
}
const context = await createTailwindContextFromEntryPoint(path, invalidate);
const conflicts = {};
const classRules = classes.reduce((classRules, className) => ({
...classRules,
[className]: context.parseCandidate(className).reduce((classRules, candidate) => {
const [rule] = context.compileAstNodes(candidate);
return {
...classRules,
...getRuleContext(rule?.node?.nodes)
};
}, {})
}), {});
for (const className in classRules) {
otherClassLoop: for (const otherClassName in classRules) {
if (className === otherClassName) {
continue otherClassLoop;
}
const classRule = classRules[className];
const otherClassRule = classRules[otherClassName];
const paths = Object.keys(classRule);
const otherPaths = Object.keys(otherClassRule);
if (paths.length !== otherPaths.length) {
continue otherClassLoop;
}
const potentialConflicts = [];
for (const path of paths) {
for (const otherPath of otherPaths) {
if (path !== otherPath) {
continue otherClassLoop;
}
if (classRule[path].length !== otherClassRule[otherPath].length) {
continue otherClassLoop;
}
for (const classRuleProperty of classRule[path]) {
for (const otherClassRuleProperty of otherClassRule[otherPath]) {
if (classRuleProperty.cssPropertyName !== otherClassRuleProperty.cssPropertyName ||
classRuleProperty.important !== otherClassRuleProperty.important) {
continue otherClassLoop;
}
potentialConflicts.push({
...classRuleProperty,
tailwindClassName: className
}, {
...otherClassRuleProperty,
tailwindClassName: otherClassName
});
}
}
}
}
conflicts[className] ?? (conflicts[className] = []);
conflicts[className].push(...potentialConflicts);
}
}
return [conflicts, warnings];
}
function getRuleContext(nodes) {
const context = {};
if (!nodes) {
return context;
}
const checkNested = (nodes, context, path = "") => {
for (const node of nodes.filter(node => !!node)) {
if (node.kind === "declaration") {
context[path] ?? (context[path] = []);
if (node.value === undefined) {
continue;
}
context[path].push({
cssPropertyName: node.property,
cssPropertyValue: node.value,
important: node.important
});
continue;
}
if (node.kind === "rule") {
return void checkNested(node.nodes, context, path + node.selector);
}
if (node.kind === "at-rule") {
return void checkNested(node.nodes, context, path + node.name + node.params);
}
}
};
checkNested(nodes, context);
return context;
}
//# sourceMappingURL=conflicting-classes.js.map