You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

394 lines
36 KiB

/*istanbul ignore start*/
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.applyPatch = applyPatch;
exports.applyPatches = applyPatches;
/*istanbul ignore end*/
var
/*istanbul ignore start*/
_string = require("../util/string")
/*istanbul ignore end*/
;
var
/*istanbul ignore start*/
_lineEndings = require("./line-endings")
/*istanbul ignore end*/
;
var
/*istanbul ignore start*/
_parse = require("./parse")
/*istanbul ignore end*/
;
var
/*istanbul ignore start*/
_distanceIterator = _interopRequireDefault(require("../util/distance-iterator"))
/*istanbul ignore end*/
;
/*istanbul ignore start*/ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
/*istanbul ignore end*/
function applyPatch(source, uniDiff) {
/*istanbul ignore start*/
var
/*istanbul ignore end*/
options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
if (typeof uniDiff === 'string') {
uniDiff =
/*istanbul ignore start*/
(0,
/*istanbul ignore end*/
/*istanbul ignore start*/
_parse
/*istanbul ignore end*/
.
/*istanbul ignore start*/
parsePatch)
/*istanbul ignore end*/
(uniDiff);
}
if (Array.isArray(uniDiff)) {
if (uniDiff.length > 1) {
throw new Error('applyPatch only works with a single input.');
}
uniDiff = uniDiff[0];
}
if (options.autoConvertLineEndings || options.autoConvertLineEndings == null) {
if (
/*istanbul ignore start*/
(0,
/*istanbul ignore end*/
/*istanbul ignore start*/
_string
/*istanbul ignore end*/
.
/*istanbul ignore start*/
hasOnlyWinLineEndings)
/*istanbul ignore end*/
(source) &&
/*istanbul ignore start*/
(0,
/*istanbul ignore end*/
/*istanbul ignore start*/
_lineEndings
/*istanbul ignore end*/
.
/*istanbul ignore start*/
isUnix)
/*istanbul ignore end*/
(uniDiff)) {
uniDiff =
/*istanbul ignore start*/
(0,
/*istanbul ignore end*/
/*istanbul ignore start*/
_lineEndings
/*istanbul ignore end*/
.
/*istanbul ignore start*/
unixToWin)
/*istanbul ignore end*/
(uniDiff);
} else if (
/*istanbul ignore start*/
(0,
/*istanbul ignore end*/
/*istanbul ignore start*/
_string
/*istanbul ignore end*/
.
/*istanbul ignore start*/
hasOnlyUnixLineEndings)
/*istanbul ignore end*/
(source) &&
/*istanbul ignore start*/
(0,
/*istanbul ignore end*/
/*istanbul ignore start*/
_lineEndings
/*istanbul ignore end*/
.
/*istanbul ignore start*/
isWin)
/*istanbul ignore end*/
(uniDiff)) {
uniDiff =
/*istanbul ignore start*/
(0,
/*istanbul ignore end*/
/*istanbul ignore start*/
_lineEndings
/*istanbul ignore end*/
.
/*istanbul ignore start*/
winToUnix)
/*istanbul ignore end*/
(uniDiff);
}
}
// Apply the diff to the input
var lines = source.split('\n'),
hunks = uniDiff.hunks,
compareLine = options.compareLine || function (lineNumber, line, operation, patchContent)
/*istanbul ignore start*/
{
return (
/*istanbul ignore end*/
line === patchContent
);
},
fuzzFactor = options.fuzzFactor || 0,
minLine = 0;
if (fuzzFactor < 0 || !Number.isInteger(fuzzFactor)) {
throw new Error('fuzzFactor must be a non-negative integer');
}
// Special case for empty patch.
if (!hunks.length) {
return source;
}
// Before anything else, handle EOFNL insertion/removal. If the patch tells us to make a change
// to the EOFNL that is redundant/impossible - i.e. to remove a newline that's not there, or add a
// newline that already exists - then we either return false and fail to apply the patch (if
// fuzzFactor is 0) or simply ignore the problem and do nothing (if fuzzFactor is >0).
// If we do need to remove/add a newline at EOF, this will always be in the final hunk:
var prevLine = '',
removeEOFNL = false,
addEOFNL = false;
for (var i = 0; i < hunks[hunks.length - 1].lines.length; i++) {
var line = hunks[hunks.length - 1].lines[i];
if (line[0] == '\\') {
if (prevLine[0] == '+') {
removeEOFNL = true;
} else if (prevLine[0] == '-') {
addEOFNL = true;
}
}
prevLine = line;
}
if (removeEOFNL) {
if (addEOFNL) {
// This means the final line gets changed but doesn't have a trailing newline in either the
// original or patched version. In that case, we do nothing if fuzzFactor > 0, and if
// fuzzFactor is 0, we simply validate that the source file has no trailing newline.
if (!fuzzFactor && lines[lines.length - 1] == '') {
return false;
}
} else if (lines[lines.length - 1] == '') {
lines.pop();
} else if (!fuzzFactor) {
return false;
}
} else if (addEOFNL) {
if (lines[lines.length - 1] != '') {
lines.push('');
} else if (!fuzzFactor) {
return false;
}
}
/**
* Checks if the hunk can be made to fit at the provided location with at most `maxErrors`
* insertions, substitutions, or deletions, while ensuring also that:
* - lines deleted in the hunk match exactly, and
* - wherever an insertion operation or block of insertion operations appears in the hunk, the
* immediately preceding and following lines of context match exactly
*
* `toPos` should be set such that lines[toPos] is meant to match hunkLines[0].
*
* If the hunk can be applied, returns an object with properties `oldLineLastI` and
* `replacementLines`. Otherwise, returns null.
*/
function applyHunk(hunkLines, toPos, maxErrors) {
/*istanbul ignore start*/
var
/*istanbul ignore end*/
hunkLinesI = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
/*istanbul ignore start*/
var
/*istanbul ignore end*/
lastContextLineMatched = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
/*istanbul ignore start*/
var
/*istanbul ignore end*/
patchedLines = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : [];
/*istanbul ignore start*/
var
/*istanbul ignore end*/
patchedLinesLength = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : 0;
var nConsecutiveOldContextLines = 0;
var nextContextLineMustMatch = false;
for (; hunkLinesI < hunkLines.length; hunkLinesI++) {
var hunkLine = hunkLines[hunkLinesI],
operation = hunkLine.length > 0 ? hunkLine[0] : ' ',
content = hunkLine.length > 0 ? hunkLine.substr(1) : hunkLine;
if (operation === '-') {
if (compareLine(toPos + 1, lines[toPos], operation, content)) {
toPos++;
nConsecutiveOldContextLines = 0;
} else {
if (!maxErrors || lines[toPos] == null) {
return null;
}
patchedLines[patchedLinesLength] = lines[toPos];
return applyHunk(hunkLines, toPos + 1, maxErrors - 1, hunkLinesI, false, patchedLines, patchedLinesLength + 1);
}
}
if (operation === '+') {
if (!lastContextLineMatched) {
return null;
}
patchedLines[patchedLinesLength] = content;
patchedLinesLength++;
nConsecutiveOldContextLines = 0;
nextContextLineMustMatch = true;
}
if (operation === ' ') {
nConsecutiveOldContextLines++;
patchedLines[patchedLinesLength] = lines[toPos];
if (compareLine(toPos + 1, lines[toPos], operation, content)) {
patchedLinesLength++;
lastContextLineMatched = true;
nextContextLineMustMatch = false;
toPos++;
} else {
if (nextContextLineMustMatch || !maxErrors) {
return null;
}
// Consider 3 possibilities in sequence:
// 1. lines contains a *substitution* not included in the patch context, or
// 2. lines contains an *insertion* not included in the patch context, or
// 3. lines contains a *deletion* not included in the patch context
// The first two options are of course only possible if the line from lines is non-null -
// i.e. only option 3 is possible if we've overrun the end of the old file.
return lines[toPos] && (applyHunk(hunkLines, toPos + 1, maxErrors - 1, hunkLinesI + 1, false, patchedLines, patchedLinesLength + 1) || applyHunk(hunkLines, toPos + 1, maxErrors - 1, hunkLinesI, false, patchedLines, patchedLinesLength + 1)) || applyHunk(hunkLines, toPos, maxErrors - 1, hunkLinesI + 1, false, patchedLines, patchedLinesLength);
}
}
}
// Before returning, trim any unmodified context lines off the end of patchedLines and reduce
// toPos (and thus oldLineLastI) accordingly. This allows later hunks to be applied to a region
// that starts in this hunk's trailing context.
patchedLinesLength -= nConsecutiveOldContextLines;
toPos -= nConsecutiveOldContextLines;
patchedLines.length = patchedLinesLength;
return {
patchedLines: patchedLines,
oldLineLastI: toPos - 1
};
}
var resultLines = [];
// Search best fit offsets for each hunk based on the previous ones
var prevHunkOffset = 0;
for (var _i = 0; _i < hunks.length; _i++) {
var hunk = hunks[_i];
var hunkResult =
/*istanbul ignore start*/
void 0
/*istanbul ignore end*/
;
var maxLine = lines.length - hunk.oldLines + fuzzFactor;
var toPos =
/*istanbul ignore start*/
void 0
/*istanbul ignore end*/
;
for (var maxErrors = 0; maxErrors <= fuzzFactor; maxErrors++) {
toPos = hunk.oldStart + prevHunkOffset - 1;
var iterator =
/*istanbul ignore start*/
(0,
/*istanbul ignore end*/
/*istanbul ignore start*/
_distanceIterator
/*istanbul ignore end*/
[
/*istanbul ignore start*/
"default"
/*istanbul ignore end*/
])(toPos, minLine, maxLine);
for (; toPos !== undefined; toPos = iterator()) {
hunkResult = applyHunk(hunk.lines, toPos, maxErrors);
if (hunkResult) {
break;
}
}
if (hunkResult) {
break;
}
}
if (!hunkResult) {
return false;
}
// Copy everything from the end of where we applied the last hunk to the start of this hunk
for (var _i2 = minLine; _i2 < toPos; _i2++) {
resultLines.push(lines[_i2]);
}
// Add the lines produced by applying the hunk:
for (var _i3 = 0; _i3 < hunkResult.patchedLines.length; _i3++) {
var _line = hunkResult.patchedLines[_i3];
resultLines.push(_line);
}
// Set lower text limit to end of the current hunk, so next ones don't try
// to fit over already patched text
minLine = hunkResult.oldLineLastI + 1;
// Note the offset between where the patch said the hunk should've applied and where we
// applied it, so we can adjust future hunks accordingly:
prevHunkOffset = toPos + 1 - hunk.oldStart;
}
// Copy over the rest of the lines from the old text
for (var _i4 = minLine; _i4 < lines.length; _i4++) {
resultLines.push(lines[_i4]);
}
return resultLines.join('\n');
}
// Wrapper that supports multiple file patches via callbacks.
function applyPatches(uniDiff, options) {
if (typeof uniDiff === 'string') {
uniDiff =
/*istanbul ignore start*/
(0,
/*istanbul ignore end*/
/*istanbul ignore start*/
_parse
/*istanbul ignore end*/
.
/*istanbul ignore start*/
parsePatch)
/*istanbul ignore end*/
(uniDiff);
}
var currentIndex = 0;
function processIndex() {
var index = uniDiff[currentIndex++];
if (!index) {
return options.complete();
}
options.loadFile(index, function (err, data) {
if (err) {
return options.complete(err);
}
var updatedContent = applyPatch(data, index, options);
options.patched(index, updatedContent, function (err) {
if (err) {
return options.complete(err);
}
processIndex();
});
});
}
processIndex();
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfc3RyaW5nIiwicmVxdWlyZSIsIl9saW5lRW5kaW5ncyIsIl9wYXJzZSIsIl9kaXN0YW5jZUl0ZXJhdG9yIiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsIm9iaiIsIl9fZXNNb2R1bGUiLCJhcHBseVBhdGNoIiwic291cmNlIiwidW5pRGlmZiIsIm9wdGlvbnMiLCJhcmd1bWVudHMiLCJsZW5ndGgiLCJ1bmRlZmluZWQiLCJwYXJzZVBhdGNoIiwiQXJyYXkiLCJpc0FycmF5IiwiRXJyb3IiLCJhdXRvQ29udmVydExpbmVFbmRpbmdzIiwiaGFzT25seVdpbkxpbmVFbmRpbmdzIiwiaXNVbml4IiwidW5peFRvV2luIiwiaGFzT25seVVuaXhMaW5lRW5kaW5ncyIsImlzV2luIiwid2luVG9Vbml4IiwibGluZXMiLCJzcGxpdCIsImh1bmtzIiwiY29tcGFyZUxpbmUiLCJsaW5lTnVtYmVyIiwibGluZSIsIm9wZXJhdGlvbiIsInBhdGNoQ29udGVudCIsImZ1enpGYWN0b3IiLCJtaW5MaW5lIiwiTnVtYmVyIiwiaXNJbnRlZ2VyIiwicHJldkxpbmUiLCJyZW1vdmVFT0ZOTCIsImFkZEVPRk5MIiwiaSIsInBvcCIsInB1c2giLCJhcHBseUh1bmsiLCJodW5rTGluZXMiLCJ0b1BvcyIsIm1heEVycm9ycyIsImh1bmtMaW5lc0kiLCJsYXN0Q29udGV4dExpbmVNYXRjaGVkIiwicGF0Y2hlZExpbmVzIiwicGF0Y2hlZExpbmVzTGVuZ3RoIiwibkNvbnNlY3V0aXZlT2xkQ29udGV4dExpbmVzIiwibmV4dENvbnRleHRMaW5lTXVzdE1hdGNoIiwiaHVua0xpbmUiLCJjb250ZW50Iiwic3Vic3RyIiwib2xkTGluZUxhc3RJIiwicmVzdWx0TGluZXMiLCJwcmV2SHVua09mZnNldCIsImh1bmsiLCJodW5rUmVzdWx0IiwibWF4TGluZSIsIm9sZExpbmVzIiwib2xkU3RhcnQiLCJpdGVyYXRvciIsImRpc3RhbmNlSXRlcmF0b3IiLCJqb2luIiwiYXBwbHlQYXRjaGVzIiwiY3VycmVudEluZGV4IiwicHJvY2Vzc0luZGV4IiwiaW5kZXgiLCJjb21wbGV0ZSIsImxvYWRGaWxlIiwiZXJyIiwiZGF0YSIsInVwZGF0ZWRDb250ZW50IiwicGF0Y2hlZCJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wYXRjaC9hcHBseS5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge2hhc09ubHlXaW5MaW5lRW5kaW5ncywgaGFzT25seVVuaXhMaW5lRW5kaW5nc30gZnJvbSAnLi4vdXRpbC9zdHJpbmcnO1xuaW1wb3J0IHtpc1dpbiwgaXNVbml4LCB1bml4VG9XaW4sIHdpblRvVW5peH0gZnJvbSAnLi9saW5lLWVuZGluZ3MnO1xuaW1wb3J0IHtwYXJzZVBhdGNofSBmcm9tICcuL3BhcnNlJztcbmltcG9ydCBkaXN0YW5jZUl0ZXJhdG9yIGZyb20gJy4uL3V0aWwvZGlzdGFuY2UtaXRlcmF0b3InO1xuXG5leHBvcnQgZnVuY3Rpb24gYXBwbHlQYXRjaChzb3VyY2UsIHVuaURpZmYsIG9wdGlvbnMgPSB7fSkge1xuICBpZiAodHlwZW9mIHVuaURpZmYgPT09ICdzdHJpbmcnKSB7XG4gICAgdW5pRGlmZiA9IHBhcnNlUGF0Y2godW5pRGlmZik7XG4gIH1cblxuICBpZiAoQXJyYXkuaXNBcnJheSh1bmlEaWZmKSkge1xuICAgIGlmICh1bmlEaWZmLmxlbmd0aCA+IDEpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignYXBwbHlQYXRjaCBvbmx5IHdvcmtzIHdpdGggYSBzaW5nbGUgaW5wdXQuJyk7XG4gICAgfVxuXG4gICAgdW5pRGlmZiA9IHVuaURpZmZbMF07XG4gIH1cblxuICBpZiAob3B0aW9ucy5hdXRvQ29udmVydExpbmVFbmRpbmdzIHx8IG9wdGlvbnMuYXV0b0NvbnZlcnRMaW5lRW5kaW5ncyA9PSBudWxsKSB7XG4gICAgaWYgKGhhc09ubHlXaW5MaW5lRW5kaW5ncyhzb3VyY2UpICYmIGlzVW5peCh1bmlEaWZmKSkge1xuICAgICAgdW5pRGlmZiA9IHVuaXhUb1dpbih1bmlEaWZmKTtcbiAgICB9IGVsc2UgaWYgKGhhc09ubHlVbml4TGluZUVuZGluZ3Moc291cmNlKSAmJiBpc1dpbih1bmlEaWZmKSkge1xuICAgICAgdW5pRGlmZiA9IHdpblRvVW5peCh1bmlEaWZmKTtcbiAgICB9XG4gIH1cblxuICAvLyBBcHBseSB0aGUgZGlmZiB0byB0aGUgaW5wdXRcbiAgbGV0IGxpbmVzID0gc291cmNlLnNwbGl0KCdcXG4nKSxcbiAgICAgIGh1bmtzID0gdW5pRGlmZi5odW5rcyxcblxuICAgICAgY29tcGFyZUxpbmUgPSBvcHRpb25zLmNvbXBhcmVMaW5lIHx8ICgobGluZU51bWJlciwgbGluZSwgb3BlcmF0aW9uLCBwYXRjaENvbnRlbnQpID0+IGxpbmUgPT09IHBhdGNoQ29udGVudCksXG4gICAgICBmdXp6RmFjdG9yID0gb3B0aW9ucy5mdXp6RmFjdG9yIHx8IDAsXG4gICAgICBtaW5MaW5lID0gMDtcblxuICBpZiAoZnV6ekZhY3RvciA8IDAgfHwgIU51bWJlci5pc0ludGVnZXIoZnV6ekZhY3RvcikpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ2Z1enpGYWN0b3IgbXVzdCBiZSBhIG5vbi1uZWdhdGl2ZSBpbnRlZ2VyJyk7XG4gIH1cblxuICAvLyBTcGVjaWFsIGNhc2UgZm9yIGVtcHR5IHBhdGNoLlxuICBpZiAoIWh1bmtzLmxlbmd0aCkge1xuICAgIHJldHVybiBzb3VyY2U7XG4gIH1cblxuICAvLyBCZWZvcmUgYW55dGhpbmcgZWxzZSwgaGFuZGxlIEVPRk5MIGluc2VydGlvbi9yZW1vdmFsLiBJZiB0aGUgcGF0Y2ggdGVsbHMgdXMgdG8gbWFrZSBhIGNoYW5nZVxuICAvLyB0byB0aGUgRU9GTkwgdGhhdCBpcyByZWR1bmRhbnQvaW1wb3NzaWJsZSAtIGkuZS4gdG8gcmVtb3ZlIGEgbmV3bGluZSB0aGF0J3Mgbm90IHRoZXJlLCBvciBhZGQgYVxuICAvLyBuZXdsaW5lIHRoYXQgYWxyZWFkeSBleGlzdHMgLSB0aGVuIHdlIGVpdGhlciByZXR1cm4gZmFsc2UgYW5kIGZhaWwgdG8gYXBwbHkgdGhlIHBhdGNoIChpZlxuICAvLyBmdXp6RmFjdG9yIGlzIDApIG9yIHNpbXBseSBpZ25vcmUgdGhlIHByb2JsZW0gYW5kIGRvIG5vdGhpbmcgKGlmIGZ1enpGYWN0b3IgaXMgPjApLlxuICAvLyBJZiB3ZSBkbyBuZWVkIHRvIHJlbW92ZS9hZGQgYSBuZXdsaW5lIGF0IEVPRiwgdGhpcyB3aWxsIGFsd2F5cyBiZSBpbiB0aGUgZmluYWwgaHVuazpcbiAgbGV0IHByZXZMaW5lID0gJycsXG4gICAgICByZW1vdmVFT0ZOTCA9IGZhbHNlLFxuICAgICAgYWRkRU9GTkwgPSBmYWxzZTtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBodW5rc1todW5rcy5sZW5ndGggLSAxXS5