import * as path from 'path';
import * as fs from 'fs';
import * as diffJs from 'diff';
import chalk from 'chalk';
// const ansiToHtml = require('ansi-to-html')
import * as fileUtils from './file-utils';
/* globals gT: true */
/* globals gIn: true */
// ansi, html.
export function getStructuredPatch({
dir,
oldFile,
newFile,
highlight = '',
}: {
dir: string;
oldFile: string;
newFile: string;
highlight?: string;
}) {
const oldPath = path.resolve(dir, oldFile);
const newPath = path.resolve(dir, newFile);
const oldText = fileUtils.safeReadFile(oldPath);
const newText = fileUtils.safeReadFile(newPath);
const structPath = diffJs.structuredPatch(oldFile, newFile, oldText, newText, '', '', {
context: 0,
newlineIsToken: false,
});
if (!highlight) {
return structPath;
}
let eol: string;
if (highlight === 'ansi') {
eol = '\n';
} else if (highlight === 'html') {
eol = '
\n';
} else {
throw new Error(`Incorrect highlight: ${highlight}`);
}
structPath.hunks.forEach(hunk => {
const oldArr = [];
const newArr = [];
for (let i = 0; i < hunk.lines.length; i++) {
const line = hunk.lines[i];
if (line.startsWith('-')) {
oldArr.push(line.slice(1) + eol);
} else if (line.startsWith('+')) {
newArr.push(line.slice(1) + eol);
} else {
// TODO: if there is not file, the same error.
throw new Error(`Unexpected line prefix:\noldPath: ${oldPath}\nnewPath: ${newPath}`);
}
}
const oldStr = oldArr.join('');
const newStr = newArr.join('');
const wordResult = diffJs.diffWordsWithSpace(oldStr, newStr);
const lineArr: string[] = [];
wordResult.forEach((word: any) => {
if (highlight === 'ansi') {
if (word.added) {
lineArr.push(chalk.green(word.value));
} else if (word.removed) {
lineArr.push(chalk.red(word.value));
} else {
lineArr.push(chalk.white(word.value));
}
} else if (word.added) {
lineArr.push(`${word.value}`);
} else if (word.removed) {
lineArr.push(`${word.value}`);
} else {
lineArr.push(`${word.value}`);
}
});
hunk.lines = [lineArr.join('')]; // eslint-disable-line no-param-reassign
});
return structPath;
}
/**
* Returns result of running external dif utility, i.e. stdout + stderr.
* NOTE: this function requires external dif utility.
* Both files must be ended by newline, otherwise there will be 'No newline at end of file' messages
* in resulting dif.
*
* @param dir - working dir
* @param oldFile - basename for file 1
* @param newFile - basename for file 2
*/
export function getDiff({
dir,
oldFile,
newFile,
highlight = '',
htmlWrap = false,
}: {
dir: string;
oldFile: string;
newFile: string;
highlight?: string;
htmlWrap?: boolean;
}) {
const structPath = getStructuredPatch({
dir,
oldFile,
newFile,
highlight,
});
if (structPath.hunks.length === 0) {
return '';
}
const strArr: string[] = [];
structPath.hunks.forEach((hunk: any) => {
strArr.push(`@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`);
hunk.lines.forEach((line: string) => {
strArr.push(line);
});
});
let eol;
let result;
if (highlight === 'html') {
eol = '
\n';
if (htmlWrap) {
result = `