UNPKG

vzcode

Version:
152 lines (132 loc) 3.4 kB
import { createTwoFilesPatch } from 'diff'; import { VizFiles, VizFileId } from '@vizhub/viz-types'; export interface UnifiedFilesDiff { [fileId: VizFileId]: string; // Unified diff string or deletion marker } // Special marker to indicate a file deletion export const FILE_DELETION_MARKER = '__FILE_DELETED__'; /** * Check if a diff string represents a file deletion */ export function isFileDeletion( diffString: string, ): boolean { return diffString.startsWith(FILE_DELETION_MARKER); } /** * Extract file name from a deletion marker */ export function getDeletedFileName( diffString: string, ): string { if (!isFileDeletion(diffString)) { return ''; } return diffString.substring( FILE_DELETION_MARKER.length + 1, ); } /** * Generate a unified diff for a single file using the diff library */ export function generateFileUnifiedDiff( fileId: VizFileId, fileName: string, beforeContent: string, afterContent: string, ): string { // Only generate diff if there are actual changes if (beforeContent === afterContent) { return ''; } // Handle file deletion case - when file existed before but is now empty/deleted if (beforeContent && !afterContent) { return `${FILE_DELETION_MARKER}:${fileName}`; } // Use the diff library's createTwoFilesPatch function to generate unified diff const unifiedDiff = createTwoFilesPatch( fileName, fileName, beforeContent, afterContent, '', '', { context: 3 }, ); return unifiedDiff; } /** * Generate unified diffs for multiple files */ export function generateFilesUnifiedDiff( beforeFiles: VizFiles, afterFiles: VizFiles, ): UnifiedFilesDiff { const result: UnifiedFilesDiff = {}; const allFileIds = new Set([ ...Object.keys(beforeFiles), ...Object.keys(afterFiles), ]); for (const fileId of allFileIds) { const beforeFile = beforeFiles[fileId]; const afterFile = afterFiles[fileId]; const beforeContent = beforeFile?.text || ''; const afterContent = afterFile?.text || ''; const fileName = afterFile?.name || beforeFile?.name || fileId; const unifiedDiff = generateFileUnifiedDiff( fileId, fileName, beforeContent, afterContent, ); // Only include files that have changes if (unifiedDiff) { result[fileId] = unifiedDiff; } } return result; } /** * Create a snapshot of current files for diff comparison */ export function createFilesSnapshot( files: VizFiles, ): VizFiles { return JSON.parse(JSON.stringify(files)); } /** * Parse unified diff to extract basic statistics */ export function parseUnifiedDiffStats( unifiedDiff: string, ): { additions: number; deletions: number; } { const lines = unifiedDiff.split('\n'); 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 }; } /** * Combine multiple unified diffs into a single diff string * Note: File deletion markers are excluded from combination */ export function combineUnifiedDiffs( unifiedDiffs: UnifiedFilesDiff, ): string { return Object.values(unifiedDiffs) .filter((diff) => !isFileDeletion(diff)) .join('\n'); }