eslint-plugin-readable-tailwind
Version:
auto-wraps tailwind classes after a certain print width or class count into multiple lines to improve readability.
323 lines • 11.7 kB
JavaScript
import { MatcherType } from "../types/rule.js";
import { getLiteralNodesByMatchers, getObjectPath, isCalleeMatchers, isCalleeName, isCalleeRegex, isInsideConditionalExpressionTest, isInsideLogicalExpressionLeft, isTagMatchers, isTagName, isTagRegex, isVariableMatchers, isVariableName, isVariableRegex, matchesPathPattern } from "../utils/matchers.js";
import { getLiteralsByESNodeAndRegex } from "../utils/regex.js";
import { deduplicateLiterals, getQuotes, getWhitespace, matchesName } from "../utils/utils.js";
export function getLiteralsByESVariableDeclarator(ctx, node, variables) {
const literals = variables.reduce((literals, variable) => {
if (!node.init) {
return literals;
}
if (!isESVariableSymbol(node.id)) {
return literals;
}
if (isVariableName(variable)) {
if (!matchesName(variable, node.id.name)) {
return literals;
}
literals.push(...getLiteralsByESExpression(ctx, [node.init]));
}
else if (isVariableRegex(variable)) {
literals.push(...getLiteralsByESNodeAndRegex(ctx, node, variable));
}
else if (isVariableMatchers(variable)) {
if (!matchesName(variable[0], node.id.name)) {
return literals;
}
literals.push(...getLiteralsByESMatchers(ctx, node.init, variable[1]));
}
return literals;
}, []);
return deduplicateLiterals(literals);
}
export function getLiteralsByESCallExpression(ctx, node, callees) {
const literals = callees.reduce((literals, callee) => {
if (!isESCalleeSymbol(node.callee)) {
return literals;
}
if (isCalleeName(callee)) {
if (!matchesName(callee, node.callee.name)) {
return literals;
}
literals.push(...getLiteralsByESExpression(ctx, node.arguments));
}
else if (isCalleeRegex(callee)) {
literals.push(...getLiteralsByESNodeAndRegex(ctx, node, callee));
}
else if (isCalleeMatchers(callee)) {
if (!matchesName(callee[0], node.callee.name)) {
return literals;
}
literals.push(...getLiteralsByESMatchers(ctx, node, callee[1]));
}
return literals;
}, []);
return deduplicateLiterals(literals);
}
export function getLiteralsByTaggedTemplateExpression(ctx, node, tags) {
const literals = tags.reduce((literals, tag) => {
if (!isTaggedTemplateSymbol(node.tag)) {
return literals;
}
if (isTagName(tag)) {
if (tag !== node.tag.name) {
return literals;
}
literals.push(...getLiteralsByESTemplateLiteral(ctx, node.quasi));
}
else if (isTagRegex(tag)) {
literals.push(...getLiteralsByESNodeAndRegex(ctx, node, tag));
}
else if (isTagMatchers(tag)) {
if (tag[0] !== node.tag.name) {
return literals;
}
literals.push(...getLiteralsByESMatchers(ctx, node, tag[1]));
}
return literals;
}, []);
return deduplicateLiterals(literals);
}
export function getLiteralsByESLiteralNode(ctx, node) {
if (isESSimpleStringLiteral(node)) {
const literal = getStringLiteralByESStringLiteral(ctx, node);
return literal ? [literal] : [];
}
if (isESTemplateLiteral(node)) {
return getLiteralsByESTemplateLiteral(ctx, node);
}
if (isESTemplateElement(node) && hasESNodeParentExtension(node)) {
const literal = getLiteralByESTemplateElement(ctx, node);
return literal ? [literal] : [];
}
return [];
}
export function getLiteralsByESMatchers(ctx, node, matchers) {
const matcherFunctions = getESMatcherFunctions(matchers);
const literalNodes = getLiteralNodesByMatchers(ctx, node, matcherFunctions);
const literals = literalNodes.reduce((literals, literalNode) => {
literals.push(...getLiteralsByESLiteralNode(ctx, literalNode));
return literals;
}, []);
return deduplicateLiterals(literals);
}
export function getLiteralNodesByRegex(ctx, node, regex) {
const sourceCode = ctx.sourceCode.getText(node);
const matchedNodes = [];
const matches = sourceCode.matchAll(regex);
for (const groups of matches) {
if (!groups.indices || groups.indices.length < 2) {
continue;
}
for (const [startIndex] of groups.indices.slice(1)) {
const literalNode = ctx.sourceCode.getNodeByRangeIndex((node.range?.[0] ?? 0) + startIndex);
if (!literalNode) {
continue;
}
matchedNodes.push(literalNode);
}
}
return matchedNodes;
}
export function getStringLiteralByESStringLiteral(ctx, node) {
const raw = node.raw;
const content = node.value;
if (!raw || !node.loc || !node.range || !node.parent.loc || !node.parent.range) {
return;
}
const quotes = getQuotes(raw);
const whitespaces = getWhitespace(content);
return {
...quotes,
...whitespaces,
content,
loc: node.loc,
node: node,
parent: node.parent,
range: node.range,
raw,
type: "StringLiteral"
};
}
function getLiteralByESTemplateElement(ctx, node) {
const raw = ctx.sourceCode.getText(node);
const content = node.value.raw;
if (!raw || !node.loc || !node.range || !node.parent.loc || !node.parent.range) {
return;
}
const quotes = getQuotes(raw);
const whitespaces = getWhitespace(content);
const braces = getBracesByString(ctx, raw);
return {
...whitespaces,
...quotes,
...braces,
content,
loc: node.loc,
node: node,
parent: node.parent,
range: node.range,
raw,
type: "TemplateLiteral"
};
}
function getLiteralsByESExpression(ctx, args) {
return args.reduce((acc, node) => {
if (node.type === "SpreadElement") {
return acc;
}
acc.push(...getLiteralsByESLiteralNode(ctx, node));
return acc;
}, []);
}
export function getLiteralsByESTemplateLiteral(ctx, node) {
return node.quasis.map(quasi => {
if (!hasESNodeParentExtension(quasi)) {
return;
}
return getLiteralByESTemplateElement(ctx, quasi);
}).filter((literal) => literal !== undefined);
}
export function findParentESTemplateLiteralByESTemplateElement(node) {
if (!hasESNodeParentExtension(node)) {
return;
}
if (node.parent.type === "TemplateLiteral") {
return node.parent;
}
return findParentESTemplateLiteralByESTemplateElement(node.parent);
}
export function isESObjectKey(node) {
return (node.parent.type === "Property" &&
node.parent.parent.type === "ObjectExpression" &&
node.parent.key === node);
}
export function isInsideObjectValue(node) {
if (!hasESNodeParentExtension(node)) {
return false;
}
// Allow call expressions as object values
if (isESCallExpression(node)) {
return false;
}
if (node.parent.type === "Property" &&
node.parent.parent.type === "ObjectExpression" &&
node.parent.value === node) {
return true;
}
return isInsideObjectValue(node.parent);
}
export function isESSimpleStringLiteral(node) {
return (node.type === "Literal" &&
"value" in node &&
typeof node.value === "string");
}
export function isESStringLike(node) {
return isESSimpleStringLiteral(node) || isESTemplateElement(node);
}
export function isESTemplateLiteral(node) {
return node.type === "TemplateLiteral";
}
export function isESTemplateElement(node) {
return node.type === "TemplateElement";
}
export function isESNode(node) {
return (node !== null &&
typeof node === "object" &&
"type" in node);
}
export function isESCallExpression(node) {
return node.type === "CallExpression";
}
function isESCalleeSymbol(node) {
return node.type === "Identifier" && !!node.parent && isESCallExpression(node.parent);
}
function isTaggedTemplateExpression(node) {
return node.type === "TaggedTemplateExpression";
}
function isTaggedTemplateSymbol(node) {
return node.type === "Identifier" && !!node.parent && isTaggedTemplateExpression(node.parent);
}
export function isESVariableDeclarator(node) {
return node.type === "VariableDeclarator";
}
function isESVariableSymbol(node) {
return node.type === "Identifier" && !!node.parent && isESVariableDeclarator(node.parent);
}
export function hasESNodeParentExtension(node) {
return "parent" in node && !!node.parent;
}
function getBracesByString(ctx, raw) {
const closingBraces = raw.startsWith("}") ? "}" : undefined;
const openingBraces = raw.endsWith("${") ? "${" : undefined;
return {
closingBraces,
openingBraces
};
}
function getESMatcherFunctions(matchers) {
return matchers.reduce((matcherFunctions, matcher) => {
switch (matcher.match) {
case MatcherType.String: {
matcherFunctions.push(node => {
if (isInsideConditionalExpressionTest(node)) {
return false;
}
if (isInsideLogicalExpressionLeft(node)) {
return false;
}
if (!hasESNodeParentExtension(node)) {
return false;
}
return (!isESObjectKey(node) &&
!isInsideObjectValue(node) &&
isESStringLike(node));
});
break;
}
case MatcherType.ObjectKey: {
matcherFunctions.push(node => {
if (isInsideConditionalExpressionTest(node)) {
return false;
}
if (isInsideLogicalExpressionLeft(node)) {
return false;
}
if (!hasESNodeParentExtension(node)) {
return false;
}
if (!isESObjectKey(node)) {
return false;
}
const path = getObjectPath(node);
return path && matcher.pathPattern ? matchesPathPattern(path, matcher.pathPattern) : true;
});
break;
}
case MatcherType.ObjectValue: {
matcherFunctions.push(node => {
if (isInsideConditionalExpressionTest(node)) {
return false;
}
if (isInsideLogicalExpressionLeft(node)) {
return false;
}
if (!hasESNodeParentExtension(node)) {
return false;
}
if (isESObjectKey(node)) {
return false;
}
const path = getObjectPath(node);
const matchesPattern = path !== undefined &&
matcher.pathPattern
? matchesPathPattern(path, matcher.pathPattern)
: true;
return isInsideObjectValue(node) && isESStringLike(node) && matchesPattern;
});
break;
}
}
return matcherFunctions;
}, []);
}
//# sourceMappingURL=es.js.map