incize
Version:
AI Commit Copilot for Power Developers
217 lines (216 loc) • 6.68 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.DiffParser = void 0;
const simple_git_1 = require("simple-git");
const types_1 = require("../../types");
class DiffParser {
git;
constructor(repoPath) {
this.git = (0, simple_git_1.simpleGit)(repoPath || process.cwd());
}
/**
* Parse diff for staged changes
*/
async parseStagedDiff() {
try {
const diff = await this.git.diff(['--cached', '--unified=3']);
return this.parseDiffString(diff);
}
catch (error) {
throw new types_1.GitError('Failed to parse staged diff', { originalError: error instanceof Error ? error.message : String(error) });
}
}
/**
* Parse diff for working directory changes
*/
async parseWorkingDiff() {
try {
const diff = await this.git.diff(['--unified=3']);
return this.parseDiffString(diff);
}
catch (error) {
throw new types_1.GitError('Failed to parse working diff', { originalError: error instanceof Error ? error.message : String(error) });
}
}
/**
* Parse diff string into structured format
*/
parseDiffString(diffString) {
if (!diffString.trim()) {
return {
files: [],
summary: { additions: 0, deletions: 0, totalChanges: 0 }
};
}
const fileDiffs = this.splitDiffIntoFiles(diffString);
const files = [];
let totalAdditions = 0;
let totalDeletions = 0;
for (const fileDiff of fileDiffs) {
const parsedFile = this.parseFileDiff(fileDiff);
if (parsedFile) {
files.push(parsedFile);
totalAdditions += parsedFile.additions;
totalDeletions += parsedFile.deletions;
}
}
return {
files,
summary: {
additions: totalAdditions,
deletions: totalDeletions,
totalChanges: totalAdditions + totalDeletions
}
};
}
/**
* Split diff string into individual file diffs
*/
splitDiffIntoFiles(diffString) {
const fileDiffs = [];
const lines = diffString.split('\n');
let currentFileDiff = [];
for (const line of lines) {
if (line.startsWith('diff --git')) {
if (currentFileDiff.length > 0) {
fileDiffs.push(currentFileDiff.join('\n'));
}
currentFileDiff = [line];
}
else {
currentFileDiff.push(line);
}
}
if (currentFileDiff.length > 0) {
fileDiffs.push(currentFileDiff.join('\n'));
}
return fileDiffs;
}
/**
* Parse individual file diff
*/
parseFileDiff(fileDiff) {
const lines = fileDiff.split('\n');
if (lines.length < 2)
return null;
// Extract file path and status
const diffHeader = lines[0];
if (!diffHeader)
return null;
const pathMatch = diffHeader.match(/diff --git a\/(.+) b\/(.+)/);
if (!pathMatch)
return null;
const oldPath = pathMatch[1];
const newPath = pathMatch[2];
const path = newPath !== '/dev/null' ? newPath : oldPath;
// Determine file status
const status = this.determineFileStatus(lines);
// Count additions and deletions
const { additions, deletions } = this.countChanges(lines);
// Detect language
const language = this.detectLanguage(path || '');
return {
path: path || '',
status,
additions,
deletions,
diff: fileDiff,
language
};
}
/**
* Determine file status from diff
*/
determineFileStatus(lines) {
for (const line of lines) {
if (line.startsWith('new file mode'))
return 'added';
if (line.startsWith('deleted file mode'))
return 'deleted';
if (line.startsWith('rename from') || line.startsWith('rename to'))
return 'renamed';
}
return 'modified';
}
/**
* Count additions and deletions in diff
*/
countChanges(lines) {
let additions = 0;
let deletions = 0;
for (const line of lines) {
if (line.startsWith('+') && !line.startsWith('+++')) {
additions++;
}
else if (line.startsWith('-') && !line.startsWith('---')) {
deletions++;
}
}
return { additions, deletions };
}
/**
* Detect programming language from file extension
*/
detectLanguage(path) {
const extension = path.split('.').pop()?.toLowerCase();
const languageMap = {
// Web
'js': 'JavaScript',
'ts': 'TypeScript',
'jsx': 'React JSX',
'tsx': 'React TSX',
'html': 'HTML',
'css': 'CSS',
'scss': 'SCSS',
'sass': 'Sass',
'vue': 'Vue',
// Backend
'py': 'Python',
'java': 'Java',
'kt': 'Kotlin',
'go': 'Go',
'rs': 'Rust',
'php': 'PHP',
'rb': 'Ruby',
'cs': 'C#',
'cpp': 'C++',
'c': 'C',
'swift': 'Swift',
// Data
'json': 'JSON',
'xml': 'XML',
'yaml': 'YAML',
'yml': 'YAML',
'toml': 'TOML',
'sql': 'SQL',
// Config
'md': 'Markdown',
'txt': 'Text',
'sh': 'Shell',
'bash': 'Bash',
'zsh': 'Shell',
'fish': 'Shell',
'ps1': 'PowerShell',
// Build
'dockerfile': 'Dockerfile',
'docker': 'Dockerfile',
'makefile': 'Makefile',
'mk': 'Makefile'
};
return languageMap[extension || ''] || extension;
}
/**
* Get summary statistics for diff
*/
async getDiffSummary() {
const diff = await this.parseStagedDiff();
const languages = [...new Set(diff.files.map(f => f.language).filter(Boolean))];
return {
fileCount: diff.files.length,
additions: diff.summary.additions,
deletions: diff.summary.deletions,
languages
};
}
}
exports.DiffParser = DiffParser;