diff --git a/release-notes.md b/release-notes.md index c3d1df85..33ea391f 100644 --- a/release-notes.md +++ b/release-notes.md @@ -4,6 +4,7 @@ - [#697](https://github.com/kpdecker/jsdiff/pull/697) *`diffJson` now correctly handles JSON objects containing a key named `__proto__`*. (Previously, the returned diff would be as if the `__proto__` key did not exist on either of the objects being diffed.) - [#700](https://github.com/kpdecker/jsdiff/pull/700) *`diffJson` now correctly handles JSON objects containing a non-callable property named `toJSON`* - i.e. it gives such a property no special behaviour whatsoever, just as `JSON.stringify` doesn't. Previously, such properties caused an error to be thrown. (*Callable* `toJSON` properties continue to get the same special behaviour that `JSON.stringify` gives them.) +- [#701](https://github.com/kpdecker/jsdiff/pull/701) *`applyPatch` with `autoConvertLineEndings` on will no longer consider a stray `\r` character occurring at the end of a file without a terminating `\n` character to be a Windows line ending*, and so will no longer strip it when converting from Windows to Unix line endings or fail to apply a Unix-style patch to a Windows file when the patch introduces such a stray `\r`. ## 9.0.0 diff --git a/src/patch/line-endings.ts b/src/patch/line-endings.ts index d36c7a59..d6c15b72 100644 --- a/src/patch/line-endings.ts +++ b/src/patch/line-endings.ts @@ -55,7 +55,7 @@ export function isUnix(patch: StructuredPatch | StructuredPatch[]): boolean { return !patch.some( index => index.hunks.some( hunk => hunk.lines.some( - line => !line.startsWith('\\') && line.endsWith('\r') + (line, i) => !line.startsWith('\\') && line.endsWith('\r') && !hunk.lines[i + 1]?.startsWith('\\') ) ) ); diff --git a/test/patch/apply.js b/test/patch/apply.js index c789488e..ec09c1d3 100755 --- a/test/patch/apply.js +++ b/test/patch/apply.js @@ -1422,6 +1422,19 @@ describe('patch/apply', function() { .to.equal(''); }); + it('should correctly apply a Unix patch whose final added line ends with a literal \\r (no newline at EOF) to a Windows file', () => { + // The patch is Unix-style (no \\r\\n line endings), but the added line's content ends with a + // literal '\\r' because the new file has no trailing newline. autoConvertLineEndings must + // recognise the patch as Unix (not Windows), convert it to match the CRLF source, and apply + // it correctly — without dropping the literal '\\r'. Previously, isUnix() returned false for + // such a patch, so no conversion was attempted and applyPatch returned false. + const oldFileUnix = 'line1\nline2\n'; + const newFileUnix = 'line1\nline3\r'; // final line has literal CR and no trailing newline + const patch = structuredPatch('test', 'test', oldFileUnix, newFileUnix, undefined, undefined, {context: 0}); + const oldFileWin = 'line1\r\nline2\r\n'; + expect(applyPatch(oldFileWin, patch)).to.equal('line1\r\nline3\r'); + }); + it('should automatically convert a patch with Unix file endings to Windows when patching a Windows file', () => { const oldFile = 'foo\r\nbar\r\nbaz\r\nqux\r\n'; const diffFile = diff --git a/test/patch/line-endings.js b/test/patch/line-endings.js index 19d318b8..7ac8650a 100644 --- a/test/patch/line-endings.js +++ b/test/patch/line-endings.js @@ -148,4 +148,19 @@ describe('isUnix', () => { ); expect(isUnix(patch)).to.equal(true); }); + + it('should still return true if only the last line in a file has a CR and there is a no newline at EOF indicator', () => { + const patch = parsePatch( + 'Index: test\n' + + '===================================================================\n' + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,2 +1,3 @@\n' + + ' line2\n' + + ' line3\n' + + '+line4\r\n' + + '\\ No newline at end of file\n' + ); + expect(isUnix(patch)).to.equal(true); + }); });