UNPKG

eslint-plugin-readable-tailwind

Version:

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

195 lines 8.29 kB
import { DEFAULT_ATTRIBUTE_NAMES, DEFAULT_CALLEE_NAMES, DEFAULT_TAG_NAMES, DEFAULT_VARIABLE_NAMES } from "../options/default-options.js"; import { ATTRIBUTE_SCHEMA, CALLEE_SCHEMA, TAG_SCHEMA, VARIABLE_SCHEMA } from "../options/descriptions.js"; import { createRuleListener } from "../utils/rule.js"; import { getCommonOptions, getExactClassLocation, splitClasses, splitWhitespaces } from "../utils/utils.js"; const defaultOptions = { attributes: DEFAULT_ATTRIBUTE_NAMES, callees: DEFAULT_CALLEE_NAMES, syntax: "parentheses", tags: DEFAULT_TAG_NAMES, variables: DEFAULT_VARIABLE_NAMES }; const DOCUMENTATION_URL = "https://github.com/schoero/eslint-plugin-better-tailwindcss/blob/main/docs/rules/enforce-consistent-variable-syntax.md"; export const enforceConsistentVariableSyntax = { name: "enforce-consistent-variable-syntax", rule: { create: ctx => createRuleListener(ctx, getOptions(ctx), lintLiterals), meta: { docs: { description: "Enforce consistent syntax for css variables.", recommended: false, url: DOCUMENTATION_URL }, fixable: "code", schema: [ { additionalProperties: false, properties: { ...CALLEE_SCHEMA, ...ATTRIBUTE_SCHEMA, ...VARIABLE_SCHEMA, ...TAG_SCHEMA, syntax: { default: "parentheses", description: "Preferred syntax for CSS variables. 'arbitrary' uses [var(--foo)], 'parentheses' uses (--foo).", enum: ["arbitrary", "parentheses"], type: "string" } }, type: "object" } ], type: "problem" } } }; function lintLiterals(ctx, literals) { for (const literal of literals) { const { syntax } = getOptions(ctx); const classChunks = splitClasses(literal.content); const whitespaceChunks = splitWhitespaces(literal.content); const startsWithWhitespace = whitespaceChunks.length > 0 && whitespaceChunks[0] !== ""; for (let classIndex = 0, literalIndex = 0; classIndex < classChunks.length; classIndex++) { const className = classChunks[classIndex]; if (startsWithWhitespace) { literalIndex += whitespaceChunks[classIndex].length; } const classStart = literalIndex; literalIndex += className.length; if (!startsWithWhitespace) { literalIndex += whitespaceChunks[classIndex + 1].length; } for (let i = 0, isInsideVar = false, isInsideBrackets = false, characters = []; i < className.length; i++) { characters.push(className[i]); if (syntax === "arbitrary") { if (isInsideVar) { continue; } // text-red-500 bg-(--brand) if (isBeginningOfParenthesizedVariable(characters)) { isInsideVar = true; const start = i + 1 - 3; const [balancedContent] = extractBalanced(className.slice(start), "(", ")"); if (!balancedContent) { continue; } const end = start + balancedContent.length + 2; const fixedVariable = `[var(${balancedContent})]`; const [literalStart] = literal.range; ctx.report({ data: { incorrectSyntax: balancedContent }, fix(fixer) { return fixer.replaceTextRange([ literalStart + classStart + start + 1, literalStart + classStart + end + 1 ], fixedVariable); }, loc: getExactClassLocation(literal, balancedContent), message: "Incorrect variable syntax: \"{{ incorrectSyntax }}\"." }); } } if (syntax === "parentheses") { if (isBeginningOfArbitraryVariable(characters)) { if (isInsideBrackets) { continue; } const start = i + 1 - (findOpeningBracketOffset(characters) ?? 0); const [balancedArbitraryContent] = extractBalanced(className.slice(start), "[", "]"); const [balancedVariableContent] = extractBalanced(className.slice(start)); if (!balancedArbitraryContent || !balancedVariableContent) { continue; } const end = start + balancedArbitraryContent.length + 2; const fixedVariable = `(${balancedVariableContent})`; const incorrectSyntax = `[${balancedArbitraryContent}]`; const [literalStart] = literal.range; ctx.report({ data: { incorrectSyntax }, fix(fixer) { return fixer.replaceTextRange([ literalStart + classStart + start + 1, literalStart + classStart + end + 1 ], fixedVariable); }, loc: getExactClassLocation(literal, incorrectSyntax, true), message: "Incorrect variable syntax: \"{{ incorrectSyntax }}\"." }); } } } } } } function isBeginningOfParenthesizedVariable(characters) { return characters.slice(-6).join("") !== "var(--" && characters.slice(-3).join("") === "(--"; } function isBeginningOfArbitraryVariable(characters) { const toBe = "[var(--".split(""); for (let i = characters.length - 1; i >= 0; i--) { if (toBe.length === 0) { return true; } const expectedChar = toBe[toBe.length - 1]; const character = characters[i]; if (i < 0) { return false; } if (character !== expectedChar) { if (expectedChar === "[" && character === "_") { continue; } return false; } toBe.pop(); } return false; } function findOpeningBracketOffset(characters) { for (let i = characters.length - 1; i >= 0; i--) { if (characters[i] === "[") { return characters.length - i; } } } function extractBalanced(className, start = "(", end = ")") { const results = []; const characters = []; for (let i = 0, parenthesesCount = 0, hasStarted = false; i < className.length; i++) { if (className[i] === start) { parenthesesCount++; if (!hasStarted) { hasStarted = true; continue; } } if (!hasStarted) { continue; } if (className[i] === end) { parenthesesCount--; if (parenthesesCount === 0) { results.push(characters.join("")); characters.length = 0; hasStarted = false; continue; } } characters.push(className[i]); } return results; } export function getOptions(ctx) { const options = ctx.options[0] ?? {}; const common = getCommonOptions(ctx); const syntax = options.syntax ?? defaultOptions.syntax; return { ...common, syntax }; } //# sourceMappingURL=enforce-consistent-variable-syntax.js.map