|
import { createTwoFilesPatch } from 'diff'; |
|
import type { FileMap } from '~/lib/stores/files'; |
|
import { MODIFICATIONS_TAG_NAME, WORK_DIR } from './constants'; |
|
|
|
export const modificationsRegex = new RegExp( |
|
`^<${MODIFICATIONS_TAG_NAME}>[\\s\\S]*?<\\/${MODIFICATIONS_TAG_NAME}>\\s+`, |
|
'g', |
|
); |
|
|
|
interface ModifiedFile { |
|
type: 'diff' | 'file'; |
|
content: string; |
|
} |
|
|
|
type FileModifications = Record<string, ModifiedFile>; |
|
|
|
export function computeFileModifications(files: FileMap, modifiedFiles: Map<string, string>) { |
|
const modifications: FileModifications = {}; |
|
|
|
let hasModifiedFiles = false; |
|
|
|
for (const [filePath, originalContent] of modifiedFiles) { |
|
const file = files[filePath]; |
|
|
|
if (file?.type !== 'file') { |
|
continue; |
|
} |
|
|
|
const unifiedDiff = diffFiles(filePath, originalContent, file.content); |
|
|
|
if (!unifiedDiff) { |
|
|
|
continue; |
|
} |
|
|
|
hasModifiedFiles = true; |
|
|
|
if (unifiedDiff.length > file.content.length) { |
|
|
|
modifications[filePath] = { type: 'file', content: file.content }; |
|
} else { |
|
|
|
modifications[filePath] = { type: 'diff', content: unifiedDiff }; |
|
} |
|
} |
|
|
|
if (!hasModifiedFiles) { |
|
return undefined; |
|
} |
|
|
|
return modifications; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function diffFiles(fileName: string, oldFileContent: string, newFileContent: string) { |
|
let unifiedDiff = createTwoFilesPatch(fileName, fileName, oldFileContent, newFileContent); |
|
|
|
const patchHeaderEnd = `--- ${fileName}\n+++ ${fileName}\n`; |
|
const headerEndIndex = unifiedDiff.indexOf(patchHeaderEnd); |
|
|
|
if (headerEndIndex >= 0) { |
|
unifiedDiff = unifiedDiff.slice(headerEndIndex + patchHeaderEnd.length); |
|
} |
|
|
|
if (unifiedDiff === '') { |
|
return undefined; |
|
} |
|
|
|
return unifiedDiff; |
|
} |
|
|
|
const regex = new RegExp(`^${WORK_DIR}\/`); |
|
|
|
|
|
|
|
|
|
export function extractRelativePath(filePath: string) { |
|
return filePath.replace(regex, ''); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function fileModificationsToHTML(modifications: FileModifications) { |
|
const entries = Object.entries(modifications); |
|
|
|
if (entries.length === 0) { |
|
return undefined; |
|
} |
|
|
|
const result: string[] = [`<${MODIFICATIONS_TAG_NAME}>`]; |
|
|
|
for (const [filePath, { type, content }] of entries) { |
|
result.push(`<${type} path=${JSON.stringify(filePath)}>`, content, `</${type}>`); |
|
} |
|
|
|
result.push(`</${MODIFICATIONS_TAG_NAME}>`); |
|
|
|
return result.join('\n'); |
|
} |
|
|