UNPKG

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
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