UNPKG

@truenine/eslint9-config

Version:

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

1 lines 10.3 kB
{"version":3,"file":"guard-clause.mjs","names":[],"sources":["../../../src/rules/code-style/guard-clause.ts"],"sourcesContent":["import type {Rule} from 'eslint'\n\n/**\n * ESLint rule: prefer-guard-clause\n * Prefer guard clauses (early returns) to reduce nesting.\n */\ninterface RuleOptions {minStatements?: number}\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n docs: {description: 'Prefer guard clauses (early returns) to reduce nesting', recommended: false},\n fixable: 'code',\n schema: [{type: 'object', properties: {minStatements: {type: 'number', default: 2}}, additionalProperties: false}],\n messages: {preferGuardClause: 'Prefer guard clause with early return to reduce nesting'}\n },\n create(context) {\n const {sourceCode} = context\n const options = (context.options[0] ?? {}) as RuleOptions\n const minStatements = options.minStatements ?? 2\n\n function isFunctionBody(node: Rule.Node): boolean {\n const {parent} = node\n return parent != null && ['FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression'].includes(parent.type)\n }\n\n function invertCondition(conditionText: string): string {\n const trimmed = conditionText.trim()\n if (trimmed.startsWith('!') && !trimmed.startsWith('!=')) {\n const inner = trimmed.slice(1).trim()\n return inner.startsWith('(') && inner.endsWith(')') ? inner.slice(1, -1) : inner\n }\n if (trimmed.includes('===')) return trimmed.replace('===', '!==')\n if (trimmed.includes('!==')) return trimmed.replace('!==', '===')\n if (trimmed.includes('==') && !trimmed.includes('===')) return trimmed.replace('==', '!=')\n if (trimmed.includes('!=') && !trimmed.includes('!==')) return trimmed.replace('!=', '==')\n if (trimmed.includes('>=')) return trimmed.replace('>=', '<')\n if (trimmed.includes('<=')) return trimmed.replace('<=', '>')\n if (trimmed.includes('>') && !trimmed.includes('>=')) return trimmed.replace('>', '<=')\n if (trimmed.includes('<') && !trimmed.includes('<=')) return trimmed.replace('<', '>=')\n\n if (/\\.length\\s*>\\s*0/.test(trimmed)) return trimmed.replace(/\\.length\\s*>\\s*0/, '.length === 0')\n if (/\\.length\\s*===\\s*0/.test(trimmed)) return trimmed.replace(/\\.length\\s*===\\s*0/, '.length > 0')\n\n return trimmed.includes('&&') || trimmed.includes('||') ? `!(${trimmed})` : `!${trimmed}`\n }\n\n function getIndent(node: Rule.Node): string { return ' '.repeat(node.loc?.start.column ?? 0) }\n function isMultiLine(node: Rule.Node): boolean { return node.loc != null && node.loc.start.line !== node.loc.end.line }\n\n function formatReturnStatement(returnText: string, indent: string): string { /* If return statement is multi-line, wrap in block */\n return returnText.includes('\\n') ? `{\\n${indent} ${returnText}\\n${indent}}` : returnText\n }\n\n return {\n IfStatement(node: any) { /* eslint-disable ts/no-unsafe-member-access, ts/no-unsafe-assignment, ts/no-unsafe-argument */\n if (node.alternate != null || node.parent?.type !== 'BlockStatement' || !isFunctionBody(node.parent) || node.consequent?.type !== 'BlockStatement') return\n const {parent} = node\n if (parent.parent?.type === 'IfStatement' && parent.parent.alternate === node) return /* Skip if already processed */\n\n const blockBody = node.consequent.body as Rule.Node[]\n if (blockBody.length < minStatements) return\n\n const parentBody = parent.body as Rule.Node[]\n const ifIndex = parentBody.indexOf(node)\n const statementsAfter = parentBody.slice(ifIndex + 1).filter(s => s.type !== 'EmptyStatement')\n\n if (statementsAfter.length === 1 && statementsAfter[0]?.type === 'ReturnStatement') { /* Case 1: if block followed by single return statement */\n const returnNode = statementsAfter[0]\n if (isMultiLine(returnNode)) return\n const lastBlockStmt = blockBody.at(-1)\n\n context.report({\n node,\n messageId: 'preferGuardClause',\n fix(fixer) {\n const indent = getIndent(node)\n const returnText = sourceCode.getText(returnNode)\n const bodyText = blockBody.map(s => sourceCode.getText(s)).join(`\\n${indent}`)\n const formattedReturn = formatReturnStatement(returnText, indent)\n const result = lastBlockStmt?.type === 'ReturnStatement' ? `if (${invertCondition(sourceCode.getText(node.test))}) ${formattedReturn}\\n\\n${indent}${bodyText}` : `if (${invertCondition(sourceCode.getText(node.test))}) ${formattedReturn}\\n\\n${indent}${bodyText}\\n${indent}${returnText}`\n\n const nodeRange = node.range as [number, number]\n return fixer.replaceTextRange([nodeRange[0], returnNode.range![1]], result)\n }\n })\n return\n }\n\n if (statementsAfter.length !== 0) return /* Case 2: if block is last statement in function */\n const lastStmt = blockBody.at(-1)\n if (lastStmt == null) return\n const endsWithReturn = lastStmt.type === 'ReturnStatement'\n let defaultReturnText = 'return'\n\n const funcParent = parent.parent\n if (funcParent?.returnType != null) {\n const returnTypeText = sourceCode.getText(funcParent.returnType)\n if (!/^\\s*:\\s*(?:void|undefined)\\s*$/.test(returnTypeText)) {\n if (endsWithReturn && (lastStmt as any).argument != null) defaultReturnText = `return ${sourceCode.getText((lastStmt as any).argument)}`\n else return /* Can't safely transform - we don't know what to return */\n }\n }\n\n context.report({\n node,\n messageId: 'preferGuardClause',\n fix(fixer) {\n const invertedCondition = invertCondition(sourceCode.getText(node.test))\n const indent = getIndent(node)\n if (endsWithReturn) {\n const statementsWithoutReturn = blockBody.slice(0, -1)\n if (statementsWithoutReturn.length === 0) return fixer.replaceText(node, `if (${invertedCondition}) ${defaultReturnText}`)\n const bodyText = statementsWithoutReturn.map(s => sourceCode.getText(s)).join(`\\n${indent}`)\n return fixer.replaceText(node, `if (${invertedCondition}) ${defaultReturnText}\\n\\n${indent}${bodyText}`)\n }\n const bodyText = blockBody.map(s => sourceCode.getText(s)).join(`\\n${indent}`)\n return fixer.replaceText(node, `if (${invertedCondition}) ${defaultReturnText}\\n\\n${indent}${bodyText}`)\n }\n })\n }\n }\n }\n}\n\nexport default rule\n"],"mappings":"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"}