{"version":3,"file":"guard-clause.cjs","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":";;AAQA,MAAM,OAAwB;CAC5B,MAAM;EACJ,MAAM;EACN,MAAM;GAAC,aAAa;GAA0D,aAAa;GAAM;EACjG,SAAS;EACT,QAAQ,CAAC;GAAC,MAAM;GAAU,YAAY,EAAC,eAAe;IAAC,MAAM;IAAU,SAAS;IAAE,EAAC;GAAE,sBAAsB;GAAM,CAAC;EAClH,UAAU,EAAC,mBAAmB,2DAA0D;EACzF;CACD,OAAO,SAAS;EACd,MAAM,EAAC,eAAc;EAErB,MAAM,iBADW,QAAQ,QAAQ,MAAM,EAAE,EACX,iBAAiB;EAE/C,SAAS,eAAe,MAA0B;GAChD,MAAM,EAAC,WAAU;AACjB,UAAO,UAAU,QAAQ;IAAC;IAAuB;IAAsB;IAA0B,CAAC,SAAS,OAAO,KAAK;;EAGzH,SAAS,gBAAgB,eAA+B;GACtD,MAAM,UAAU,cAAc,MAAM;AACpC,OAAI,QAAQ,WAAW,IAAI,IAAI,CAAC,QAAQ,WAAW,KAAK,EAAE;IACxD,MAAM,QAAQ,QAAQ,MAAM,EAAE,CAAC,MAAM;AACrC,WAAO,MAAM,WAAW,IAAI,IAAI,MAAM,SAAS,IAAI,GAAG,MAAM,MAAM,GAAG,GAAG,GAAG;;AAE7E,OAAI,QAAQ,SAAS,MAAM,CAAE,QAAO,QAAQ,QAAQ,OAAO,MAAM;AACjE,OAAI,QAAQ,SAAS,MAAM,CAAE,QAAO,QAAQ,QAAQ,OAAO,MAAM;AACjE,OAAI,QAAQ,SAAS,KAAK,IAAI,CAAC,QAAQ,SAAS,MAAM,CAAE,QAAO,QAAQ,QAAQ,MAAM,KAAK;AAC1F,OAAI,QAAQ,SAAS,KAAK,IAAI,CAAC,QAAQ,SAAS,MAAM,CAAE,QAAO,QAAQ,QAAQ,MAAM,KAAK;AAC1F,OAAI,QAAQ,SAAS,KAAK,CAAE,QAAO,QAAQ,QAAQ,MAAM,IAAI;AAC7D,OAAI,QAAQ,SAAS,KAAK,CAAE,QAAO,QAAQ,QAAQ,MAAM,IAAI;AAC7D,OAAI,QAAQ,SAAS,IAAI,IAAI,CAAC,QAAQ,SAAS,KAAK,CAAE,QAAO,QAAQ,QAAQ,KAAK,KAAK;AACvF,OAAI,QAAQ,SAAS,IAAI,IAAI,CAAC,QAAQ,SAAS,KAAK,CAAE,QAAO,QAAQ,QAAQ,KAAK,KAAK;AAEvF,OAAI,mBAAmB,KAAK,QAAQ,CAAE,QAAO,QAAQ,QAAQ,oBAAoB,gBAAgB;AACjG,OAAI,qBAAqB,KAAK,QAAQ,CAAE,QAAO,QAAQ,QAAQ,sBAAsB,cAAc;AAEnG,UAAO,QAAQ,SAAS,KAAK,IAAI,QAAQ,SAAS,KAAK,GAAG,KAAK,QAAQ,KAAK,IAAI;;EAGlF,SAAS,UAAU,MAAyB;AAAE,UAAO,IAAI,OAAO,KAAK,KAAK,MAAM,UAAU,EAAE;;EAC5F,SAAS,YAAY,MAA0B;AAAE,UAAO,KAAK,OAAO,QAAQ,KAAK,IAAI,MAAM,SAAS,KAAK,IAAI,IAAI;;EAEjH,SAAS,sBAAsB,YAAoB,QAAwB;AACzE,UAAO,WAAW,SAAS,KAAK,GAAG,MAAM,OAAO,IAAI,WAAW,IAAI,OAAO,KAAK;;AAGjF,SAAO,EACL,YAAY,MAAW;AACrB,OAAI,KAAK,aAAa,QAAQ,KAAK,QAAQ,SAAS,oBAAoB,CAAC,eAAe,KAAK,OAAO,IAAI,KAAK,YAAY,SAAS,iBAAkB;GACpJ,MAAM,EAAC,WAAU;AACjB,OAAI,OAAO,QAAQ,SAAS,iBAAiB,OAAO,OAAO,cAAc,KAAM;GAE/E,MAAM,YAAY,KAAK,WAAW;AAClC,OAAI,UAAU,SAAS,cAAe;GAEtC,MAAM,aAAa,OAAO;GAC1B,MAAM,UAAU,WAAW,QAAQ,KAAK;GACxC,MAAM,kBAAkB,WAAW,MAAM,UAAU,EAAE,CAAC,QAAO,MAAK,EAAE,SAAS,iBAAiB;AAE9F,OAAI,gBAAgB,WAAW,KAAK,gBAAgB,IAAI,SAAS,mBAAmB;IAClF,MAAM,aAAa,gBAAgB;AACnC,QAAI,YAAY,WAAW,CAAE;IAC7B,MAAM,gBAAgB,UAAU,GAAG,GAAG;AAEtC,YAAQ,OAAO;KACb;KACA,WAAW;KACX,IAAI,OAAO;MACT,MAAM,SAAS,UAAU,KAAK;MAC9B,MAAM,aAAa,WAAW,QAAQ,WAAW;MACjD,MAAM,WAAW,UAAU,KAAI,MAAK,WAAW,QAAQ,EAAE,CAAC,CAAC,KAAK,KAAK,SAAS;MAC9E,MAAM,kBAAkB,sBAAsB,YAAY,OAAO;MACjE,MAAM,SAAS,eAAe,SAAS,oBAAoB,OAAO,gBAAgB,WAAW,QAAQ,KAAK,KAAK,CAAC,CAAC,IAAI,gBAAgB,MAAM,SAAS,aAAa,OAAO,gBAAgB,WAAW,QAAQ,KAAK,KAAK,CAAC,CAAC,IAAI,gBAAgB,MAAM,SAAS,SAAS,IAAI,SAAS;MAEhR,MAAM,YAAY,KAAK;AACvB,aAAO,MAAM,iBAAiB,CAAC,UAAU,IAAI,WAAW,MAAO,GAAG,EAAE,OAAO;;KAE9E,CAAC;AACF;;AAGF,OAAI,gBAAgB,WAAW,EAAG;GAClC,MAAM,WAAW,UAAU,GAAG,GAAG;AACjC,OAAI,YAAY,KAAM;GACtB,MAAM,iBAAiB,SAAS,SAAS;GACzC,IAAI,oBAAoB;GAExB,MAAM,aAAa,OAAO;AAC1B,OAAI,YAAY,cAAc,MAAM;IAClC,MAAM,iBAAiB,WAAW,QAAQ,WAAW,WAAW;AAChE,QAAI,CAAC,iCAAiC,KAAK,eAAe,CACxD,KAAI,kBAAmB,SAAiB,YAAY,KAAM,qBAAoB,UAAU,WAAW,QAAS,SAAiB,SAAS;QACjI;;AAIT,WAAQ,OAAO;IACb;IACA,WAAW;IACX,IAAI,OAAO;KACT,MAAM,oBAAoB,gBAAgB,WAAW,QAAQ,KAAK,KAAK,CAAC;KACxE,MAAM,SAAS,UAAU,KAAK;AAC9B,SAAI,gBAAgB;MAClB,MAAM,0BAA0B,UAAU,MAAM,GAAG,GAAG;AACtD,UAAI,wBAAwB,WAAW,EAAG,QAAO,MAAM,YAAY,MAAM,OAAO,kBAAkB,IAAI,oBAAoB;MAC1H,MAAM,WAAW,wBAAwB,KAAI,MAAK,WAAW,QAAQ,EAAE,CAAC,CAAC,KAAK,KAAK,SAAS;AAC5F,aAAO,MAAM,YAAY,MAAM,OAAO,kBAAkB,IAAI,kBAAkB,MAAM,SAAS,WAAW;;KAE1G,MAAM,WAAW,UAAU,KAAI,MAAK,WAAW,QAAQ,EAAE,CAAC,CAAC,KAAK,KAAK,SAAS;AAC9E,YAAO,MAAM,YAAY,MAAM,OAAO,kBAAkB,IAAI,kBAAkB,MAAM,SAAS,WAAW;;IAE3G,CAAC;KAEL;;CAEJ"}