{"version":3,"file":"control.cjs","names":[],"sources":["../../../src/rules/single-line/control.ts"],"sourcesContent":["import type {Rule} from 'eslint'\n\nconst MAX_LINE_LENGTH = 160\n\n/**\n * ESLint rule: prefer-single-line-control\n * Prefer single-line switch cases, for loops, while loops, and try-catch when possible.\n */\nconst rule: Rule.RuleModule = {\n  meta: {\n    type: 'layout',\n    docs: {description: 'Prefer single-line switch cases, for loops, while loops, and try-catch when possible', recommended: false},\n    fixable: 'code',\n    schema: [],\n    messages: {\n      preferSingleLineCase: 'Switch case with simple statement should be single-line format',\n      preferSingleLineFor: 'For loop with simple body should be single-line format',\n      preferSingleLineWhile: 'While loop with simple body should be single-line format',\n      preferSingleLineTry: 'Try-catch with simple bodies should be single-line format',\n      preferBraceCatch: '} catch should be on the same line',\n      preferBraceFinally: '} finally should be on the same line'\n    }\n  },\n  create(context) {\n    const {sourceCode} = context\n\n    function getSingleStatement(node: Rule.Node | null | undefined): Rule.Node | null {\n      if (!node) return null\n      if (node.type !== 'BlockStatement') return node\n      const {body} = node as Rule.Node & {body: Rule.Node[]}\n      return Array.isArray(body) && body.length === 1 ? body[0] : null\n    }\n\n    function hasComments(node: Rule.Node): boolean { return sourceCode.getCommentsInside(node).length > 0 }\n\n    function isSimpleStatement(stmt: Rule.Node | null): boolean {\n      if (!stmt) return false\n      return new Set(['ExpressionStatement', 'ReturnStatement', 'ThrowStatement', 'BreakStatement', 'ContinueStatement']).has(stmt.type)\n    }\n\n    function isNodeSingleLine(node: Rule.Node): boolean { return node.loc?.start.line === node.loc?.end.line }\n    function normalizeText(text: string): string { return text.split('\\n').map(l => l.trim()).join(' ').replaceAll(/\\s+/g, ' ').trim() }\n    function ensureSemicolon(text: string): string { return text.trimEnd().endsWith(';') ? text.trimEnd() : `${text.trimEnd()};` }\n\n    type CaseNode = Rule.Node & {test: Rule.Node | null, consequent: Rule.Node[]}\n    type LoopNode = Rule.Node & {body: Rule.Node}\n    type WhileNode = LoopNode & {test: Rule.Node}\n\n    function extractCaseStatements(consequent: Rule.Node[]): {mainStmt: Rule.Node | null, hasBreak: boolean} { /* 从 case 的 consequent 中提取语句和 break */\n      if (!Array.isArray(consequent) || consequent.length === 0) return {mainStmt: null, hasBreak: false}\n      if (consequent.length === 1) {\n        if (isSimpleStatement(consequent[0])) return {mainStmt: consequent[0], hasBreak: false}\n      }\n      if (consequent.length === 2 && isSimpleStatement(consequent[0]) && consequent[1].type === 'BreakStatement') return {mainStmt: consequent[0], hasBreak: true}\n\n      if (consequent.length !== 1 || consequent[0].type !== 'BlockStatement') return {mainStmt: null, hasBreak: false}\n      const {body} = consequent[0] as Rule.Node & {body: Rule.Node[]}\n      if (!Array.isArray(body) || body.length === 0 || body.length > 2) return {mainStmt: null, hasBreak: false}\n      const hasBreak = body.length === 2 && body[1].type === 'BreakStatement'\n      return (body.length === 1 || hasBreak) && isSimpleStatement(body[0]) ? {mainStmt: body[0], hasBreak} : {mainStmt: null, hasBreak: false}\n    }\n\n    function canCaseBeSimplified(caseNode: CaseNode): boolean {\n      const {consequent, test} = caseNode\n      const {mainStmt, hasBreak} = extractCaseStatements(consequent)\n      if (!mainStmt || !isNodeSingleLine(mainStmt) || hasComments(caseNode)) return false\n      const testText = test != null ? normalizeText(sourceCode.getText(test)) : 'default'\n      return `case ${testText}: ${ensureSemicolon(sourceCode.getText(mainStmt))}${hasBreak ? ' break' : ''}`.length < MAX_LINE_LENGTH\n    }\n\n    function canLoopBeSimplified(loopNode: LoopNode): boolean {\n      const {body} = loopNode\n      if (body?.type !== 'BlockStatement' || hasComments(body)) return false\n      const stmt = getSingleStatement(body)\n      if (!stmt || !isSimpleStatement(stmt) || !isNodeSingleLine(stmt)) return false\n      const headerText = normalizeText(sourceCode.getText(loopNode).slice(0, (body.range?.[0] ?? 0) - (loopNode.range?.[0] ?? 0)))\n      return `${headerText} ${sourceCode.getText(stmt)}`.length < MAX_LINE_LENGTH\n    }\n\n    function isLoopAlreadySingleLine(l: LoopNode): boolean { return l.body == null || l.body.type === 'BlockStatement' ? false : l.loc?.start.line === l.body.loc?.end.line }\n\n    const loopVisitor = (messageId: 'preferSingleLineFor' | 'preferSingleLineWhile') => (node: Rule.Node) => {\n      const l = node as LoopNode\n      if (isLoopAlreadySingleLine(l) || !canLoopBeSimplified(l)) return\n      context.report({\n        node,\n        messageId,\n        fix: fixer => {\n          const stmt = getSingleStatement(l.body)!\n          if (node.type === 'WhileStatement') return fixer.replaceText(node, `while (${normalizeText(sourceCode.getText((node as WhileNode).test))}) ${sourceCode.getText(stmt).trimEnd()}`)\n          const h = normalizeText(sourceCode.getText(node).slice(0, (l.body.range?.[0] ?? 0) - (node.range?.[0] ?? 0)))\n          return fixer.replaceText(node, `${h} ${sourceCode.getText(stmt).trimEnd()}`)\n        }\n      })\n    }\n\n    return {\n      SwitchStatement(node) {\n        const {cases} = node as Rule.Node & {cases: CaseNode[]}\n        cases?.forEach(c => {\n          if (c.loc?.start.line !== c.loc?.end.line && canCaseBeSimplified(c)) {\n            context.report({\n              node: c,\n              messageId: 'preferSingleLineCase',\n              fix: fixer => {\n                const {mainStmt, hasBreak} = extractCaseStatements(c.consequent)\n                const tText = c.test ? `case ${normalizeText(sourceCode.getText(c.test))}:` : 'default:'\n                return fixer.replaceText(c, `${tText} ${ensureSemicolon(sourceCode.getText(mainStmt!))}${hasBreak ? ' break' : ''}`)\n              }\n            })\n          }\n        })\n      },\n      ForStatement: loopVisitor('preferSingleLineFor'),\n      ForInStatement: loopVisitor('preferSingleLineFor'),\n      ForOfStatement: loopVisitor('preferSingleLineFor'),\n      WhileStatement: loopVisitor('preferSingleLineWhile'),\n      DoWhileStatement() { /* do-while 结构特殊，暂不处理 */ }\n    }\n  }\n}\n\nexport default rule\n"],"mappings":"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"}