UNPKG

@truenine/eslint9-config

Version:

ESLint 9 configuration package for Compose Client projects with TypeScript, Vue, and modern JavaScript support

211 lines (209 loc) 8.18 kB
//#region src/rules/single-line/if.ts const MAX_LINE_LENGTH = 160; const rule = { meta: { type: "layout", docs: { description: "Prefer single-line if statements when possible", recommended: false }, fixable: "code", schema: [], messages: { preferSingleLine: "If-else chain with simple statements should be single-line format", preferSingleLineBranch: "Simple if/else branch should be single-line format" } }, create(context) { const { sourceCode } = context; function getSingleStatement(node) { if (!node) return null; if (node.type !== "BlockStatement") return node; const { body } = node; if (!Array.isArray(body) || body.length !== 1) return null; return body[0]; } function hasComments(node) { return sourceCode.getCommentsInside(node).length > 0; } function isSimpleStatement(stmt) { if (!stmt) return false; return new Set([ "ExpressionStatement", "ReturnStatement", "ThrowStatement", "BreakStatement", "ContinueStatement" ]).has(stmt.type); } function isNodeSingleLine(node) { return node.loc?.start.line === node.loc?.end.line; } function normalizeConditionText(text) { return text.split("\n").map((line) => line.trim()).join(" ").replaceAll(/\s+/g, " ").trim(); } function canConditionBeSingleLine(test) { return normalizeConditionText(sourceCode.getText(test)).length < MAX_LINE_LENGTH - 40; } function collectIfChain(node) { const conditions = []; let current = node; while (current && current.type === "IfStatement") { conditions.push(current); current = "alternate" in current ? current.alternate : null; } return { conditions, finalElse: current }; } function canBranchBeSimplified(consequent, test) { if (consequent.type !== "BlockStatement") return false; if (!canConditionBeSingleLine(test)) return false; if (hasComments(consequent)) return false; const stmt = getSingleStatement(consequent); if (!stmt || !isSimpleStatement(stmt)) return false; if (!isNodeSingleLine(stmt)) return false; return `if (${normalizeConditionText(sourceCode.getText(test))}) ${sourceCode.getText(stmt)}`.length < MAX_LINE_LENGTH; } function canConvertToSingleLine(node) { const { conditions, finalElse } = collectIfChain(node); for (const cond of conditions) { if (!("consequent" in cond) || !("test" in cond)) return false; const test = cond.test; if (!canConditionBeSingleLine(test)) return false; const consequent = cond.consequent; if (hasComments(consequent)) return false; const stmt = getSingleStatement(consequent); if (!stmt || !isSimpleStatement(stmt)) return false; if (!isNodeSingleLine(stmt)) return false; } if (finalElse) { if (hasComments(finalElse)) return false; const stmt = getSingleStatement(finalElse); if (!stmt || !isSimpleStatement(stmt)) return false; if (!isNodeSingleLine(stmt)) return false; } const lines = []; for (let i = 0; i < conditions.length; i++) { const cond = conditions[i]; if (!("consequent" in cond) || !("test" in cond)) continue; const stmt = getSingleStatement(cond.consequent); const condText = normalizeConditionText(sourceCode.getText(cond.test)); const stmtText = sourceCode.getText(stmt); const prefix = i === 0 ? "if" : "else if"; lines.push(`${prefix} (${condText}) ${stmtText}`); } if (!finalElse) return lines.every((line) => line.length < MAX_LINE_LENGTH); const stmt = getSingleStatement(finalElse); lines.push(`else ${sourceCode.getText(stmt)}`); return lines.every((line) => line.length < MAX_LINE_LENGTH); } function isAlreadySingleLine(node) { const { conditions, finalElse } = collectIfChain(node); for (const cond of conditions) { if (!("consequent" in cond)) return false; if (cond.consequent.type === "BlockStatement") return false; } if (finalElse && finalElse.type === "BlockStatement") return false; for (const cond of conditions) { if (!("consequent" in cond)) return false; const consequent = cond.consequent; if (cond.loc?.start.line !== consequent.loc?.end.line) return false; } return true; } function isBranchAlreadySingleLine(consequent, ifNode) { if (consequent.type === "BlockStatement") return false; return ifNode.loc?.start.line === consequent.loc?.end.line; } return { IfStatement(node) { const { parent } = node; if (parent?.type === "IfStatement" && "alternate" in parent && parent.alternate === node) return; if (!isAlreadySingleLine(node) && canConvertToSingleLine(node)) { context.report({ node, messageId: "preferSingleLine", fix(fixer) { const { conditions, finalElse } = collectIfChain(node); const indent = " ".repeat(node.loc?.start.column ?? 0); const lines = []; for (let i = 0; i < conditions.length; i++) { const cond = conditions[i]; if (!("consequent" in cond) || !("test" in cond)) continue; const stmt = getSingleStatement(cond.consequent); const condText = normalizeConditionText(sourceCode.getText(cond.test)); let stmtText = sourceCode.getText(stmt); if (!stmtText.endsWith(";") && !stmtText.endsWith("}")) stmtText = stmtText.trimEnd(); const prefix = i === 0 ? "if" : `${indent}else if`; lines.push(`${prefix} (${condText}) ${stmtText}`); } if (!finalElse) return fixer.replaceText(node, lines.join("\n")); const stmt = getSingleStatement(finalElse); let stmtText = sourceCode.getText(stmt); if (!stmtText.endsWith(";") && !stmtText.endsWith("}")) stmtText = stmtText.trimEnd(); lines.push(`${indent}else ${stmtText}`); return fixer.replaceText(node, lines.join("\n")); } }); return; } const { conditions, finalElse } = collectIfChain(node); const branchSimplifiable = []; for (const cond of conditions) { if (!("consequent" in cond) || !("test" in cond)) { branchSimplifiable.push(false); continue; } const consequent = cond.consequent; const test = cond.test; const canSimplify = !isBranchAlreadySingleLine(consequent, cond) && canBranchBeSimplified(consequent, test); branchSimplifiable.push(canSimplify); } let finalElseSimplifiable = false; if (finalElse && finalElse.type === "BlockStatement" && !hasComments(finalElse)) { const stmt = getSingleStatement(finalElse); if (stmt && isSimpleStatement(stmt) && isNodeSingleLine(stmt)) finalElseSimplifiable = sourceCode.getText(stmt).length < MAX_LINE_LENGTH - 20; } if (!(branchSimplifiable.some(Boolean) || finalElseSimplifiable)) return; context.report({ node, messageId: "preferSingleLineBranch", fix(fixer) { const indent = " ".repeat(node.loc?.start.column ?? 0); const lines = []; for (let i = 0; i < conditions.length; i++) { const cond = conditions[i]; if (!("consequent" in cond) || !("test" in cond)) continue; const consequent = cond.consequent; const test = cond.test; const condText = sourceCode.getText(test); const prefix = i === 0 ? "if" : `${indent}else if`; if (branchSimplifiable[i]) { const stmt = getSingleStatement(consequent); let stmtText = sourceCode.getText(stmt); if (!stmtText.endsWith(";") && !stmtText.endsWith("}")) stmtText = stmtText.trimEnd(); lines.push(`${prefix} (${condText}) ${stmtText}`); } else { const consequentText = sourceCode.getText(consequent); lines.push(`${prefix} (${condText}) ${consequentText}`); } } if (finalElse) if (finalElseSimplifiable) { const stmt = getSingleStatement(finalElse); let stmtText = sourceCode.getText(stmt); if (!stmtText.endsWith(";") && !stmtText.endsWith("}")) stmtText = stmtText.trimEnd(); lines.push(`${indent}else ${stmtText}`); } else { const elseText = sourceCode.getText(finalElse); lines.push(`${indent}else ${elseText}`); } return fixer.replaceText(node, lines.join("\n")); } }); } }; } }; //#endregion module.exports = rule; //# sourceMappingURL=if.cjs.map