diff --git a/packages/netlify-cms-core/src/formats/__tests__/frontmatter.spec.js b/packages/netlify-cms-core/src/formats/__tests__/frontmatter.spec.js index 2ea344523375..eef5d7a88827 100644 --- a/packages/netlify-cms-core/src/formats/__tests__/frontmatter.spec.js +++ b/packages/netlify-cms-core/src/formats/__tests__/frontmatter.spec.js @@ -179,14 +179,14 @@ describe('Frontmatter', () => { 'title: YAML', '---', 'Some content', - 'On another line\n', + 'On another line', ].join('\n'), ); }); it('should stringify YAML with missing body', () => { expect(FrontmatterInfer.toFile({ tags: ['front matter', 'yaml'], title: 'YAML' })).toEqual( - ['---', 'tags:', ' - front matter', ' - yaml', 'title: YAML', '---', '', ''].join('\n'), + ['---', 'tags:', ' - front matter', ' - yaml', 'title: YAML', '---', ''].join('\n'), ); }); @@ -206,7 +206,7 @@ describe('Frontmatter', () => { 'title: YAML', '---', 'Some content', - 'On another line\n', + 'On another line', ].join('\n'), ); }); @@ -227,7 +227,7 @@ describe('Frontmatter', () => { 'title: YAML', '~~~', 'Some content', - 'On another line\n', + 'On another line', ].join('\n'), ); }); @@ -248,7 +248,7 @@ describe('Frontmatter', () => { 'title: YAML', '^^^', 'Some content', - 'On another line\n', + 'On another line', ].join('\n'), ); }); @@ -267,7 +267,7 @@ describe('Frontmatter', () => { 'title = "TOML"', '+++', 'Some content', - 'On another line\n', + 'On another line', ].join('\n'), ); }); @@ -286,7 +286,7 @@ describe('Frontmatter', () => { 'title = "TOML"', '~~~', 'Some content', - 'On another line\n', + 'On another line', ].join('\n'), ); }); @@ -308,7 +308,7 @@ describe('Frontmatter', () => { ' "title": "JSON"', '}', 'Some content', - 'On another line\n', + 'On another line', ].join('\n'), ); }); @@ -330,8 +330,24 @@ describe('Frontmatter', () => { ' "title": "JSON"', '~~~', 'Some content', - 'On another line\n', + 'On another line', ].join('\n'), ); }); + + it('should trim last line break if added by grey-matter', () => { + expect( + frontmatterYAML().toFile({ + body: 'noLineBreak', + }), + ).toEqual('noLineBreak'); + }); + + it('should not trim last line break if not added by grey-matter', () => { + expect( + frontmatterYAML().toFile({ + body: 'withLineBreak\n', + }), + ).toEqual('withLineBreak\n'); + }); }); diff --git a/packages/netlify-cms-core/src/formats/frontmatter.js b/packages/netlify-cms-core/src/formats/frontmatter.js index 309f32ce5103..a95dc3ed73fe 100644 --- a/packages/netlify-cms-core/src/formats/frontmatter.js +++ b/packages/netlify-cms-core/src/formats/frontmatter.js @@ -70,6 +70,8 @@ class FrontmatterFormatter { const format = this.format || inferFrontmatterFormat(content); if (this.customDelimiter) this.format.delimiters = this.customDelimiter; const result = matter(content, { engines: parsers, ...format }); + // in the absent of a body when serializing an entry we use an empty one + // when calling `toFile`, so we don't want to add it when parsing. return { ...result.data, ...(result.content.trim() && { body: result.content }), @@ -83,8 +85,13 @@ class FrontmatterFormatter { const format = this.format || getFormatOpts('yaml'); if (this.customDelimiter) this.format.delimiters = this.customDelimiter; + // gray-matter always adds a line break at the end which trips our + // change detection logic + // https://github.com/jonschlinkert/gray-matter/issues/96 + const trimLastLineBreak = body.slice(-1) !== '\n' ? true : false; // `sortedKeys` is not recognized by gray-matter, so it gets passed through to the parser - return matter.stringify(body, meta, { engines: parsers, sortedKeys, ...format }); + const file = matter.stringify(body, meta, { engines: parsers, sortedKeys, ...format }); + return trimLastLineBreak && file.slice(-1) === '\n' ? file.substring(0, file.length - 1) : file; } }