react-diff-viewer-continued
Version:
Continuation of a simple and beautiful text diff viewer component made with diff and React
229 lines (228 loc) • 9.96 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.computeLineInformation = exports.DiffMethod = exports.DiffType = void 0;
const diff = __importStar(require("diff"));
const jsDiff = diff;
var DiffType;
(function (DiffType) {
DiffType[DiffType["DEFAULT"] = 0] = "DEFAULT";
DiffType[DiffType["ADDED"] = 1] = "ADDED";
DiffType[DiffType["REMOVED"] = 2] = "REMOVED";
DiffType[DiffType["CHANGED"] = 3] = "CHANGED";
})(DiffType || (exports.DiffType = DiffType = {}));
// See https://github.com/kpdecker/jsdiff/tree/v4.0.1#api for more info on the below JsDiff methods
var DiffMethod;
(function (DiffMethod) {
DiffMethod["CHARS"] = "diffChars";
DiffMethod["WORDS"] = "diffWords";
DiffMethod["WORDS_WITH_SPACE"] = "diffWordsWithSpace";
DiffMethod["LINES"] = "diffLines";
DiffMethod["TRIMMED_LINES"] = "diffTrimmedLines";
DiffMethod["SENTENCES"] = "diffSentences";
DiffMethod["CSS"] = "diffCss";
DiffMethod["JSON"] = "diffJson";
})(DiffMethod || (exports.DiffMethod = DiffMethod = {}));
/**
* Splits diff text by new line and computes final list of diff lines based on
* conditions.
*
* @param value Diff text from the js diff module.
*/
const constructLines = (value) => {
if (value === '')
return [];
const lines = value.replace(/\n$/, '').split('\n');
return lines;
};
/**
* Computes word diff information in the line.
* [TODO]: Consider adding options argument for JsDiff text block comparison
*
* @param oldValue Old word in the line.
* @param newValue New word in the line.
* @param compareMethod JsDiff text diff method from https://github.com/kpdecker/jsdiff/tree/v4.0.1#api
*/
const computeDiff = (oldValue, newValue, compareMethod = DiffMethod.CHARS) => {
const diffArray = jsDiff[compareMethod](oldValue, newValue);
const computedDiff = {
left: [],
right: [],
};
diffArray.forEach(({ added, removed, value }) => {
const diffInformation = {};
if (added) {
diffInformation.type = DiffType.ADDED;
diffInformation.value = value;
computedDiff.right.push(diffInformation);
}
if (removed) {
diffInformation.type = DiffType.REMOVED;
diffInformation.value = value;
computedDiff.left.push(diffInformation);
}
if (!removed && !added) {
diffInformation.type = DiffType.DEFAULT;
diffInformation.value = value;
computedDiff.right.push(diffInformation);
computedDiff.left.push(diffInformation);
}
return diffInformation;
});
return computedDiff;
};
/**
* [TODO]: Think about moving common left and right value assignment to a
* common place. Better readability?
*
* Computes line wise information based in the js diff information passed. Each
* line contains information about left and right section. Left side denotes
* deletion and right side denotes addition.
*
* @param oldString Old string to compare.
* @param newString New string to compare with old string.
* @param disableWordDiff Flag to enable/disable word diff.
* @param lineCompareMethod JsDiff text diff method from https://github.com/kpdecker/jsdiff/tree/v4.0.1#api
* @param linesOffset line number to start counting from
* @param showLines lines that are always shown, regardless of diff
*/
const computeLineInformation = (oldString, newString, disableWordDiff = false, lineCompareMethod = DiffMethod.CHARS, linesOffset = 0, showLines = []) => {
let diffArray = [];
// Use diffLines for strings, and diffJson for objects...
if (typeof oldString === 'string' && typeof newString === 'string') {
diffArray = diff.diffLines(oldString.trimRight(), newString.trimRight(), {
newlineIsToken: false,
ignoreWhitespace: false,
ignoreCase: false,
});
}
else {
diffArray = diff.diffJson(oldString, newString);
}
let rightLineNumber = linesOffset;
let leftLineNumber = linesOffset;
let lineInformation = [];
let counter = 0;
const diffLines = [];
const ignoreDiffIndexes = [];
const getLineInformation = (value, diffIndex, added, removed, evaluateOnlyFirstLine) => {
const lines = constructLines(value);
return lines
.map((line, lineIndex) => {
const left = {};
const right = {};
if (ignoreDiffIndexes.includes(`${diffIndex}-${lineIndex}`) ||
(evaluateOnlyFirstLine && lineIndex !== 0)) {
return undefined;
}
if (added || removed) {
let countAsChange = true;
if (removed) {
leftLineNumber += 1;
left.lineNumber = leftLineNumber;
left.type = DiffType.REMOVED;
left.value = line || ' ';
// When the current line is of type REMOVED, check the next item in
// the diff array whether it is of type ADDED. If true, the current
// diff will be marked as both REMOVED and ADDED. Meaning, the
// current line is a modification.
const nextDiff = diffArray[diffIndex + 1];
if (nextDiff && nextDiff.added) {
const nextDiffLines = constructLines(nextDiff.value)[lineIndex];
if (nextDiffLines) {
const nextDiffLineInfo = getLineInformation(nextDiffLines, diffIndex, true, false, true);
const { value: rightValue, lineNumber, type, } = nextDiffLineInfo[0].right;
// When identified as modification, push the next diff to ignore
// list as the next value will be added in this line computation as
// right and left values.
ignoreDiffIndexes.push(`${diffIndex + 1}-${lineIndex}`);
right.lineNumber = lineNumber;
if (left.value === rightValue) {
// The new value is exactly the same as the old
countAsChange = false;
right.type = 0;
left.type = 0;
right.value = rightValue;
}
else {
right.type = type;
// Do char level diff and assign the corresponding values to the
// left and right diff information object.
if (disableWordDiff) {
right.value = rightValue;
}
else {
const computedDiff = computeDiff(line, rightValue, lineCompareMethod);
right.value = computedDiff.right;
left.value = computedDiff.left;
}
}
}
}
}
else {
rightLineNumber += 1;
right.lineNumber = rightLineNumber;
right.type = DiffType.ADDED;
right.value = line;
}
if (countAsChange && !evaluateOnlyFirstLine) {
if (!diffLines.includes(counter)) {
diffLines.push(counter);
}
}
}
else {
leftLineNumber += 1;
rightLineNumber += 1;
left.lineNumber = leftLineNumber;
left.type = DiffType.DEFAULT;
left.value = line;
right.lineNumber = rightLineNumber;
right.type = DiffType.DEFAULT;
right.value = line;
}
if ((showLines === null || showLines === void 0 ? void 0 : showLines.includes(`L-${left.lineNumber}`)) || (showLines === null || showLines === void 0 ? void 0 : showLines.includes(`R-${right.lineNumber}`)) && !diffLines.includes(counter)) {
diffLines.push(counter);
}
if (!evaluateOnlyFirstLine) {
counter += 1;
}
return { right, left };
})
.filter(Boolean);
};
diffArray.forEach(({ added, removed, value }, index) => {
lineInformation = [
...lineInformation,
...getLineInformation(value, index, added, removed),
];
});
return {
lineInformation,
diffLines,
};
};
exports.computeLineInformation = computeLineInformation;