UNPKG

eslint-plugin-readable-tailwind

Version:

auto-wraps tailwind classes after a certain print width or class count into multiple lines to improve readability.

157 lines 7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.tailwindNoDuplicateClasses = void 0; const readable_tailwind_options_default_options_js_1 = require("../options/default-options.js"); const readable_tailwind_options_descriptions_js_1 = require("../options/descriptions.js"); const readable_tailwind_parsers_es_js_1 = require("../parsers/es.js"); const readable_tailwind_utils_quotes_js_1 = require("../utils/quotes.js"); const readable_tailwind_utils_rule_js_1 = require("../utils/rule.js"); const readable_tailwind_utils_utils_js_1 = require("../utils/utils.js"); const defaultOptions = { attributes: readable_tailwind_options_default_options_js_1.DEFAULT_ATTRIBUTE_NAMES, callees: readable_tailwind_options_default_options_js_1.DEFAULT_CALLEE_NAMES, tags: readable_tailwind_options_default_options_js_1.DEFAULT_TAG_NAMES, variables: readable_tailwind_options_default_options_js_1.DEFAULT_VARIABLE_NAMES }; exports.tailwindNoDuplicateClasses = { name: "no-duplicate-classes", rule: { create: ctx => (0, readable_tailwind_utils_rule_js_1.createRuleListener)(ctx, getOptions(ctx), lintLiterals), meta: { docs: { category: "Stylistic Issues", description: "Disallow duplicate class names in tailwind classes.", recommended: true, url: "https://github.com/schoero/eslint-plugin-readable-tailwind/blob/main/docs/rules/no-duplicate-classes.md" }, fixable: "code", schema: [ { additionalProperties: false, properties: { ...readable_tailwind_options_descriptions_js_1.CALLEE_SCHEMA, ...readable_tailwind_options_descriptions_js_1.ATTRIBUTE_SCHEMA, ...readable_tailwind_options_descriptions_js_1.VARIABLE_SCHEMA, ...readable_tailwind_options_descriptions_js_1.TAG_SCHEMA }, type: "object" } ], type: "layout" } } }; function lintLiterals(ctx, literals) { for (const literal of literals) { const esNode = ctx.sourceCode.getNodeByRangeIndex(literal.range[0]); const parentLiteralNodes = esNode && findParentLiteralNodes(esNode); const parentLiterals = parentLiteralNodes && getLiteralsFromParentLiteralNodes(parentLiteralNodes, literals); const parentClasses = parentLiterals ? getClassesFromLiteralNodes(parentLiterals) : []; const duplicates = []; const classes = literal.content; const classChunks = (0, readable_tailwind_utils_utils_js_1.splitClasses)(classes); const whitespaceChunks = (0, readable_tailwind_utils_utils_js_1.splitWhitespaces)(classes); const finalChunks = []; const startsWithWhitespace = whitespaceChunks.length > 0 && whitespaceChunks[0] !== ""; const endsWithWhitespace = whitespaceChunks.length > 0 && whitespaceChunks[whitespaceChunks.length - 1] !== ""; for (let i = 0; i < whitespaceChunks.length; i++) { if (whitespaceChunks[i]) { finalChunks.push(whitespaceChunks[i]); } if (classChunks[i]) { // always push sticky classes without adding to the register if (!startsWithWhitespace && i === 0 && literal.closingBraces || !endsWithWhitespace && i === classChunks.length - 1 && literal.openingBraces) { finalChunks.push(classChunks[i]); continue; } if (parentClasses.includes(classChunks[i])) { if (!duplicates.includes(classChunks[i])) { duplicates.push(classChunks[i]); } } else { finalChunks.push(classChunks[i]); parentClasses.push(classChunks[i]); } } } const escapedClasses = (0, readable_tailwind_utils_quotes_js_1.escapeNestedQuotes)(finalChunks.join(""), literal.openingQuote ?? "\""); const fixedClasses = [ literal.openingQuote ?? "", literal.type === "TemplateLiteral" && literal.closingBraces ? literal.closingBraces : "", escapedClasses, literal.type === "TemplateLiteral" && literal.openingBraces ? literal.openingBraces : "", literal.closingQuote ?? "" ].join(""); if (literal.raw === fixedClasses) { continue; } ctx.report({ data: { duplicateClassname: duplicates.join(", ") }, fix(fixer) { return fixer.replaceTextRange(literal.range, fixedClasses); }, loc: literal.loc, message: "Duplicate classname: \"{{ duplicateClassname }}\"." }); } } function findParentLiteralNodes(node) { if (!(0, readable_tailwind_parsers_es_js_1.hasESNodeParentExtension)(node)) { return; } const parentLiterals = []; let currentNode = node; while ((0, readable_tailwind_parsers_es_js_1.hasESNodeParentExtension)(currentNode)) { const parent = currentNode.parent; if ((0, readable_tailwind_parsers_es_js_1.isESCallExpression)(parent)) { break; } if ((0, readable_tailwind_parsers_es_js_1.isESVariableDeclarator)(parent)) { break; } if (parent.type === "TemplateLiteral") { for (const quasi of parent.quasis) { if (quasi.range === node.range) { break; } if (quasi.type === "TemplateElement") { parentLiterals.push(quasi); } } } if (parent.type === "TemplateElement" || parent.type === "Literal") { parentLiterals.push(parent); } currentNode = parent; } return parentLiterals; } function getLiteralsFromParentLiteralNodes(parentLiteralNodes, literals) { return parentLiteralNodes.map(parentLiteralNode => { return literals.find(literal => literal.range === parentLiteralNode.range); }); } function getClassesFromLiteralNodes(literals) { return literals.reduce((combinedClasses, literal) => { if (!literal) { return combinedClasses; } const classes = literal.content; const split = (0, readable_tailwind_utils_utils_js_1.splitClasses)(classes); for (const className of split) { if (!combinedClasses.includes(className)) { combinedClasses.push(className); } } return combinedClasses; }, []); } function getOptions(ctx) { return (0, readable_tailwind_utils_utils_js_1.getCommonOptions)(ctx); } //# sourceMappingURL=tailwind-no-duplicate-classes.js.map