eslint-plugin-better-tailwindcss
Version:
auto-wraps tailwind classes after a certain print width or class count into multiple lines to improve readability.
84 lines • 3.32 kB
JavaScript
import { readFile } from "node:fs/promises";
import { dirname } from "node:path";
import { fork } from "@eslint/css-tree";
import { tailwind4 } from "tailwind-csstree";
import { withCache } from "../async-utils/cache.js";
import { resolveCss } from "../async-utils/resolvers.js";
const { findAll, generate, parse } = fork(tailwind4);
export async function getCustomComponentClasses(ctx) {
const resolvedPath = resolveCss(ctx, ctx.tailwindConfigPath);
const files = await parseCssFilesDeep(ctx, resolvedPath);
const utilities = Object.values(files).reduce((customComponentClasses, { ast }) => {
customComponentClasses.push(...getCustomComponentUtilities(ast));
return customComponentClasses;
}, []);
return utilities;
}
async function parseCssFilesDeep(ctx, resolvedPath) {
const cssFiles = {};
const cssFile = await parseCssFile(ctx, resolvedPath);
if (!cssFile) {
return cssFiles;
}
cssFiles[resolvedPath] = cssFile;
for (const importPath of cssFile.imports) {
const importedFiles = await parseCssFilesDeep(ctx, importPath);
for (const importedFile in importedFiles) {
cssFiles[importedFile] = importedFiles[importedFile];
}
}
return cssFiles;
}
const parseCssFile = async (ctx, resolvedPath) => withCache("css-file", resolvedPath, async () => {
try {
const content = await readFile(resolvedPath, "utf-8");
const ast = parse(content);
const importNodes = findAll(ast, node => node.type === "Atrule" &&
node.name === "import" &&
node.prelude?.type === "AtrulePrelude");
const imports = importNodes.reduce((imports, importNode) => {
if (importNode.type !== "Atrule" || !importNode.prelude) {
return imports;
}
const importStatement = generate(importNode.prelude).match(/["'](?<importPath>[^"']+)["']/);
if (!importStatement) {
return imports;
}
const { importPath } = importStatement.groups || {};
const cwd = dirname(resolvedPath);
const resolvedImportPath = resolveCss(ctx, importPath, cwd);
if (resolvedImportPath) {
imports.push(resolvedImportPath);
}
return imports;
}, []);
return {
ast,
imports
};
}
catch { }
});
function getCustomComponentUtilities(ast) {
const customComponentUtilities = [];
const componentLayers = findAll(ast, node => {
return node.type === "Atrule" &&
node.name === "layer" &&
node.prelude?.type === "AtrulePrelude" &&
generate(node.prelude).trim() === "components";
});
for (const layer of componentLayers) {
const classSelectors = findAll(layer, node => node.type === "ClassSelector");
for (const classNode of classSelectors) {
if (classNode.type !== "ClassSelector") {
continue;
}
if (customComponentUtilities.includes(classNode.name)) {
continue;
}
customComponentUtilities.push(classNode.name);
}
}
return customComponentUtilities;
}
//# sourceMappingURL=custom-component-classes.async.v4.js.map