UNPKG

review-copilot

Version:

ReviewCopilot - AI-powered code review assistant with customizable prompts

225 lines (224 loc) • 9.68 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.printSingleReviewResult = printSingleReviewResult; exports.formatReviewResultsAsMarkdown = formatReviewResultsAsMarkdown; exports.outputReviewResults = outputReviewResults; exports.outputToCIPlatform = outputToCIPlatform; exports.outputToTerminal = outputToTerminal; exports.printReviewResultsToTerminal = printReviewResultsToTerminal; const chalk_1 = __importDefault(require("chalk")); const environment_helpers_1 = __importDefault(require("../../utils/environment-helpers")); const logger_1 = require("../../utils/logger"); function printSingleReviewResult(result) { const hasIssues = result.suggestions.length > 0 || result.error !== undefined; if (!hasIssues && result.success) { return; } if (result.suggestions.length > 0) { result.suggestions.forEach((suggestion) => { console.log(`${chalk_1.default.blue(suggestion.message)}`); }); } if (result.error) { console.log(` 🚨 ${chalk_1.default.red(result.error.message)}`); } console.log(''); } function formatReviewResultsAsMarkdown(results, context) { if (!Array.isArray(results) || results.length === 0) { return 'āœ… No issues found in this review.\n'; } let comment = ''; if (context?.type === 'branchName' && context.branchName) { comment += `**Branch:** \`${context.branchName}\`\n\n`; } if (context?.type === 'commitMessages' && context.commits && context.prDetails) { context.commits.forEach((commit) => { const sha = commit.hash; const url = `https://github.com/${context.prDetails.owner}/${context.prDetails.repo}/commit/${sha}`; comment += `- [${sha.slice(0, 7)}](${url}): ${commit.message}\n`; }); comment += '\n'; } const resultsWithIssues = results.filter((result) => result.suggestions.length > 0 || result.error !== undefined || !result.success); if (resultsWithIssues.length > 0) { resultsWithIssues.forEach((result) => { if (result.suggestions.length > 0) { result.suggestions.forEach((suggestion) => { comment += `${suggestion.message}\n`; }); } if (result.error) { comment += `🚨 ${result.error.message}\n`; } comment += '\n'; }); } return comment; } async function outputReviewResults(context, results, extraContext) { context.spinner.text = 'Processing results...'; if (environment_helpers_1.default.isCI) { await outputToCIPlatform(context, results, extraContext); } else { await outputToTerminal(results, extraContext); } } async function outputToCIPlatform(context, results, extraContext) { context.spinner.text = 'Posting review comments...'; if (!context.gitService || !context.prDetails) { throw new Error('Git service or PR details not found'); } if (results.branchName) { const body = formatReviewResultsAsMarkdown(results.branchName, { type: 'branchName', branchName: extraContext?.branchName, }); await context.gitService.createIssueComment({ owner: context.prDetails.owner, repo: context.prDetails.repo, issue_number: context.prDetails.pullNumber, body, }); } if (results.commitMessages) { const body = formatReviewResultsAsMarkdown(results.commitMessages, { type: 'commitMessages', commits: extraContext?.commits, prDetails: context.prDetails, }); await context.gitService.createIssueComment({ owner: context.prDetails.owner, repo: context.prDetails.repo, issue_number: context.prDetails.pullNumber, body, }); } if (results.codeChanges) { // Skip creating review comment if there are no actual code changes to review const hasActualCodeChanges = results.codeChanges.some((result) => result.suggestions.some((suggestion) => suggestion.filename && (suggestion.line ?? 0) > 0)); if (!hasActualCodeChanges) { // Log the message but don't create a review comment console.log(chalk_1.default.gray('\nšŸ“‹ No code changes to review. Skipping review comment creation.')); return; } // Separate general and line-specific suggestions const generalSuggestions = results.codeChanges.filter((result) => result.suggestions.some((suggestion) => suggestion.reviewType !== 'line-specific')); const lineSpecificSuggestions = results.codeChanges.filter((result) => result.suggestions.some((suggestion) => suggestion.reviewType === 'line-specific')); // Create general review comment if there are general suggestions if (generalSuggestions.length > 0) { const body = formatReviewResultsAsMarkdown(generalSuggestions); const reviewCommentParams = { owner: context.prDetails.owner, repo: context.prDetails.repo, pullNumber: context.prDetails.pullNumber, body, commitId: context.prDetails.commitId, path: context.prDetails.path, }; await context.gitService.createReviewComment(reviewCommentParams); } // Create line-specific review comments for (const result of lineSpecificSuggestions) { for (const suggestion of result.suggestions) { if (suggestion.reviewType === 'line-specific' && suggestion.line && suggestion.filename) { const body = formatReviewResultsAsMarkdown([ { ...result, suggestions: [suggestion], }, ]); // Base parameters for all review comments const reviewCommentParams = { owner: context.prDetails.owner, repo: context.prDetails.repo, pullNumber: context.prDetails.pullNumber, body, commitId: context.prDetails.commitId, path: suggestion.filename, line: suggestion.line, side: 'RIGHT', }; // Add multi-line parameters if startLine is provided and different from line if (suggestion.startLine && suggestion.startLine !== suggestion.line) { // Ensure startLine is less than line for multi-line comments const startLine = Math.min(suggestion.startLine, suggestion.line); const endLine = Math.max(suggestion.startLine, suggestion.line); Object.assign(reviewCommentParams, { startLine, startSide: 'RIGHT', line: endLine, }); } await context.gitService.createReviewComment(reviewCommentParams); } } } } } async function outputToTerminal(results, extraContext) { let hasAnyResults = false; if (results.branchName) { logger_1.Logger.info('\n🌿 Branch Name Review Results:'); logger_1.Logger.divider(); printReviewResultsToTerminal(results.branchName, { type: 'branchName', branchName: extraContext?.branchName, }); hasAnyResults = true; } if (results.commitMessages && results.commitMessages.length > 0) { logger_1.Logger.info('\nšŸ“ Commit Messages Review Results:'); logger_1.Logger.divider(); printReviewResultsToTerminal(results.commitMessages, { type: 'commitMessages', commits: extraContext?.commits, prDetails: extraContext?.prDetails, }); hasAnyResults = true; } if (results.codeChanges) { logger_1.Logger.info('\nšŸ” Code Changes Review Results:'); logger_1.Logger.divider(); printReviewResultsToTerminal(results.codeChanges); hasAnyResults = true; } if (!hasAnyResults) { logger_1.Logger.gray('\nšŸ“‹ No review results to display.'); } } function printReviewResultsToTerminal(results, context) { if (!Array.isArray(results) || results.length === 0) { logger_1.Logger.gray('\nšŸ“ No review results to display.\n'); return; } if (context?.type === 'branchName' && context.branchName) { logger_1.Logger.success(`Branch: ${context.branchName}`); } if (context?.type === 'commitMessages' && context.commits && context.prDetails) { context.commits.forEach((commit) => { const sha = commit.hash; const url = `https://github.com/${context.prDetails.owner}/${context.prDetails.repo}/commit/${sha}`; logger_1.Logger.yellow(`[${sha.slice(0, 7)}] ${commit.message}`); logger_1.Logger.gray(url); }); logger_1.Logger.white(''); } logger_1.Logger.info('\nšŸ“ Review Results:\n'); results.forEach((result) => { printSingleReviewResult(result); }); }