Custom delimiters (#1064)

* frontmatter functions now exported as curried constructors

* changed formats.js to accomodate frontmatter.js changes

* can only use custom delimiters if format is specified

* reverting changes

* updated tests to reflect changes

* created tests for custom delimiters

* updated docs to reflect addition of custom delimiter configuration

* formatByExtension no longer takes custom delimiter

* missed some formatByExtension calls

* Edited the docs according to suggestions

* Throws an error if a custom delimiter is set without declaring frontmatter format

* Throws error if custom delimiter set without explicit and proper frontmatter format being declared

* example config file set to proper defaults

* moved frontmatterFormats to formats

* Update .all-contributorsrc

* updated contributions

* Clarify docs
This commit is contained in:
Swieckowski 2018-02-08 20:04:42 -05:00 committed by Caleb
parent f2d19f241b
commit afce6abbc2
7 changed files with 138 additions and 35 deletions

View File

@ -710,6 +710,17 @@
"code" "code"
] ]
}, },
{
"login": "Swieckowski",
"name": "Swieckowski",
"avatar_url": "https://avatars0.githubusercontent.com/u/31023010?v=4",
"profile": "https://www.linkedin.com/in/arthur-swieckowski/",
"contributions": [
"code",
"doc",
"test"
]
},
{ {
"login": "pixelastic", "login": "pixelastic",
"name": "Tim Carry", "name": "Tim Carry",

View File

@ -1,6 +1,5 @@
# Netlify CMS # Netlify CMS
[![All Contributors](https://img.shields.io/badge/all_contributors-87-orange.svg)](#contributors)
[![All Contributors](https://img.shields.io/badge/all_contributors-86-orange.svg)](#contributors)
[![Open Source Helpers](https://www.codetriage.com/netlify/netlify-cms/badges/users.svg)](https://www.codetriage.com/netlify/netlify-cms) [![Open Source Helpers](https://www.codetriage.com/netlify/netlify-cms/badges/users.svg)](https://www.codetriage.com/netlify/netlify-cms)
[![](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/netlify/netlifycms) [![](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/netlify/netlifycms)
@ -62,8 +61,8 @@ Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds
| [<img src="https://avatars0.githubusercontent.com/u/250958?v=4" width="100px;"/><br /><sub><b>Etienne Bernard</b></sub>](https://github.com/ebernard)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=ebernard "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/871660?v=4" width="100px;"/><br /><sub><b>Eli Williamson</b></sub>](http://eliwilliamson.com)<br />[💻](https://github.com/netlify/netlify-cms/commits?author=eliwilliamson "Code") [🎨](#design-eliwilliamson "Design") | [<img src="https://avatars0.githubusercontent.com/u/858837?v=4" width="100px;"/><br /><sub><b>Covington Doan</b></sub>](https://www.covingtondoan.com)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=covingtondoan "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/1299080?v=4" width="100px;"/><br /><sub><b>Lennart Ziburski</b></sub>](http://lennartziburski.com)<br />[💻](https://github.com/netlify/netlify-cms/commits?author=ziburski "Code") [🎨](#design-ziburski "Design") [📖](https://github.com/netlify/netlify-cms/commits?author=ziburski "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/387640?v=4" width="100px;"/><br /><sub><b>Darrel O'Pry</b></sub>](http://darrelopry.com)<br />[💻](https://github.com/netlify/netlify-cms/commits?author=dopry "Code") [🚇](#infra-dopry "Infrastructure (Hosting, Build-Tools, etc)") [⚠️](https://github.com/netlify/netlify-cms/commits?author=dopry "Tests") | [<img src="https://avatars1.githubusercontent.com/u/13856868?v=4" width="100px;"/><br /><sub><b>Erin Symons</b></sub>](https://github.com/yourfrienderin)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=yourfrienderin "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/5200555?v=4" width="100px;"/><br /><sub><b>Austin Green</b></sub>](https://github.com/AustinGreen)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=AustinGreen "Documentation") [💡](#example-AustinGreen "Examples") | | [<img src="https://avatars0.githubusercontent.com/u/250958?v=4" width="100px;"/><br /><sub><b>Etienne Bernard</b></sub>](https://github.com/ebernard)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=ebernard "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/871660?v=4" width="100px;"/><br /><sub><b>Eli Williamson</b></sub>](http://eliwilliamson.com)<br />[💻](https://github.com/netlify/netlify-cms/commits?author=eliwilliamson "Code") [🎨](#design-eliwilliamson "Design") | [<img src="https://avatars0.githubusercontent.com/u/858837?v=4" width="100px;"/><br /><sub><b>Covington Doan</b></sub>](https://www.covingtondoan.com)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=covingtondoan "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/1299080?v=4" width="100px;"/><br /><sub><b>Lennart Ziburski</b></sub>](http://lennartziburski.com)<br />[💻](https://github.com/netlify/netlify-cms/commits?author=ziburski "Code") [🎨](#design-ziburski "Design") [📖](https://github.com/netlify/netlify-cms/commits?author=ziburski "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/387640?v=4" width="100px;"/><br /><sub><b>Darrel O'Pry</b></sub>](http://darrelopry.com)<br />[💻](https://github.com/netlify/netlify-cms/commits?author=dopry "Code") [🚇](#infra-dopry "Infrastructure (Hosting, Build-Tools, etc)") [⚠️](https://github.com/netlify/netlify-cms/commits?author=dopry "Tests") | [<img src="https://avatars1.githubusercontent.com/u/13856868?v=4" width="100px;"/><br /><sub><b>Erin Symons</b></sub>](https://github.com/yourfrienderin)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=yourfrienderin "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/5200555?v=4" width="100px;"/><br /><sub><b>Austin Green</b></sub>](https://github.com/AustinGreen)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=AustinGreen "Documentation") [💡](#example-AustinGreen "Examples") |
| [<img src="https://avatars2.githubusercontent.com/u/799360?v=4" width="100px;"/><br /><sub><b>Bryan Robinson</b></sub>](http://bryanlrobinson.com)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=brob "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/561983?v=4" width="100px;"/><br /><sub><b>Darren</b></sub>](https://github.com/dardub)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=dardub "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/576512?v=4" width="100px;"/><br /><sub><b>Richard Pullinger</b></sub>](http://www.richardpullinger.com)<br />[💻](https://github.com/netlify/netlify-cms/commits?author=rpullinger "Code") | [<img src="https://avatars1.githubusercontent.com/u/3718939?v=4" width="100px;"/><br /><sub><b>Taylor Bryant</b></sub>](https://taylorbryant.blog)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=taylorbryant "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/5881826?v=4" width="100px;"/><br /><sub><b>kvanerkelens</b></sub>](https://github.com/kvanerkelens)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=kvanerkelens "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/8291663?v=4" width="100px;"/><br /><sub><b>Patrick Sier</b></sub>](https://patsier.com/)<br />[💻](https://github.com/netlify/netlify-cms/commits?author=pjsier "Code") | [<img src="https://avatars2.githubusercontent.com/u/6132191?v=4" width="100px;"/><br /><sub><b>Christian Nolte</b></sub>](http://noltech.net)<br />[💻](https://github.com/netlify/netlify-cms/commits?author=drlogout "Code") | | [<img src="https://avatars2.githubusercontent.com/u/799360?v=4" width="100px;"/><br /><sub><b>Bryan Robinson</b></sub>](http://bryanlrobinson.com)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=brob "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/561983?v=4" width="100px;"/><br /><sub><b>Darren</b></sub>](https://github.com/dardub)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=dardub "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/576512?v=4" width="100px;"/><br /><sub><b>Richard Pullinger</b></sub>](http://www.richardpullinger.com)<br />[💻](https://github.com/netlify/netlify-cms/commits?author=rpullinger "Code") | [<img src="https://avatars1.githubusercontent.com/u/3718939?v=4" width="100px;"/><br /><sub><b>Taylor Bryant</b></sub>](https://taylorbryant.blog)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=taylorbryant "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/5881826?v=4" width="100px;"/><br /><sub><b>kvanerkelens</b></sub>](https://github.com/kvanerkelens)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=kvanerkelens "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/8291663?v=4" width="100px;"/><br /><sub><b>Patrick Sier</b></sub>](https://patsier.com/)<br />[💻](https://github.com/netlify/netlify-cms/commits?author=pjsier "Code") | [<img src="https://avatars2.githubusercontent.com/u/6132191?v=4" width="100px;"/><br /><sub><b>Christian Nolte</b></sub>](http://noltech.net)<br />[💻](https://github.com/netlify/netlify-cms/commits?author=drlogout "Code") |
| [<img src="https://avatars1.githubusercontent.com/u/3818?v=4" width="100px;"/><br /><sub><b>Edward Betts</b></sub>](http://edwardbetts.com/)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=EdwardBetts "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/8771435?v=4" width="100px;"/><br /><sub><b>Josh Hardman</b></sub>](https://github.com/jhardman0830)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=jhardman0830 "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/11616378?v=4" width="100px;"/><br /><sub><b>Mantas</b></sub>](https://behance.net/mistermantas)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=mistermantas "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/5244986?v=4" width="100px;"/><br /><sub><b>Marco Biedermann</b></sub>](https://www.marcobiedermann.com)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=marcobiedermann "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/7525670?v=4" width="100px;"/><br /><sub><b>Max Stoiber</b></sub>](https://mxstbr.blog/newsletter)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=mxstbr "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/89368?v=4" width="100px;"/><br /><sub><b>Vyacheslav Matyukhin</b></sub>](http://berekuk.ru)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=berekuk "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/9059048?v=4" width="100px;"/><br /><sub><b>jimmaaay</b></sub>](https://jimmythompson.me)<br />[💻](https://github.com/netlify/netlify-cms/commits?author=jimmaaay "Code") | | [<img src="https://avatars1.githubusercontent.com/u/3818?v=4" width="100px;"/><br /><sub><b>Edward Betts</b></sub>](http://edwardbetts.com/)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=EdwardBetts "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/8771435?v=4" width="100px;"/><br /><sub><b>Josh Hardman</b></sub>](https://github.com/jhardman0830)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=jhardman0830 "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/11616378?v=4" width="100px;"/><br /><sub><b>Mantas</b></sub>](https://behance.net/mistermantas)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=mistermantas "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/5244986?v=4" width="100px;"/><br /><sub><b>Marco Biedermann</b></sub>](https://www.marcobiedermann.com)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=marcobiedermann "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/7525670?v=4" width="100px;"/><br /><sub><b>Max Stoiber</b></sub>](https://mxstbr.blog/newsletter)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=mxstbr "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/89368?v=4" width="100px;"/><br /><sub><b>Vyacheslav Matyukhin</b></sub>](http://berekuk.ru)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=berekuk "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/9059048?v=4" width="100px;"/><br /><sub><b>jimmaaay</b></sub>](https://jimmythompson.me)<br />[💻](https://github.com/netlify/netlify-cms/commits?author=jimmaaay "Code") |
| [<img src="https://avatars3.githubusercontent.com/u/802086?v=4" width="100px;"/><br /><sub><b>Luís Miguel</b></sub>](https://github.com/Quicksaver)<br />[🐛](https://github.com/netlify/netlify-cms/issues?q=author%3AQuicksaver "Bug reports") [💻](https://github.com/netlify/netlify-cms/commits?author=Quicksaver "Code") | [<img src="https://avatars2.githubusercontent.com/u/357379?v=4" width="100px;"/><br /><sub><b>Chris Swithinbank</b></sub>](http://chrisswithinbank.net/)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=delucis "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/1262221?v=4" width="100px;"/><br /><sub><b>remmah</b></sub>](https://github.com/remmah)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=remmah "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/29388?v=4" width="100px;"/><br /><sub><b>Sumeet Jain</b></sub>](http://sumeetjain.com)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=sumeetjain "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/26816046?v=4" width="100px;"/><br /><sub><b>Sagar Khatri</b></sub>](https://github.com/ragasirtahk)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=ragasirtahk "Documentation") [💡](#example-ragasirtahk "Examples") | [<img src="https://avatars0.githubusercontent.com/u/8182932?v=4" width="100px;"/><br /><sub><b>Kevin Doocey</b></sub>](https://www.dooceykev.in)<br />[💻](https://github.com/netlify/netlify-cms/commits?author=Doocey "Code") | [<img src="https://avatars2.githubusercontent.com/u/283419?v=4" width="100px;"/><br /><sub><b>Tim Carry</b></sub>](http://www.pixelastic.com/)<br />[💻](https://github.com/netlify/netlify-cms/commits?author=pixelastic "Code") [🎨](#design-pixelastic "Design") [📖](https://github.com/netlify/netlify-cms/commits?author=pixelastic "Documentation") | | [<img src="https://avatars3.githubusercontent.com/u/802086?v=4" width="100px;"/><br /><sub><b>Luís Miguel</b></sub>](https://github.com/Quicksaver)<br />[🐛](https://github.com/netlify/netlify-cms/issues?q=author%3AQuicksaver "Bug reports") [💻](https://github.com/netlify/netlify-cms/commits?author=Quicksaver "Code") | [<img src="https://avatars2.githubusercontent.com/u/357379?v=4" width="100px;"/><br /><sub><b>Chris Swithinbank</b></sub>](http://chrisswithinbank.net/)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=delucis "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/1262221?v=4" width="100px;"/><br /><sub><b>remmah</b></sub>](https://github.com/remmah)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=remmah "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/29388?v=4" width="100px;"/><br /><sub><b>Sumeet Jain</b></sub>](http://sumeetjain.com)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=sumeetjain "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/26816046?v=4" width="100px;"/><br /><sub><b>Sagar Khatri</b></sub>](https://github.com/ragasirtahk)<br />[📖](https://github.com/netlify/netlify-cms/commits?author=ragasirtahk "Documentation") [💡](#example-ragasirtahk "Examples") | [<img src="https://avatars0.githubusercontent.com/u/8182932?v=4" width="100px;"/><br /><sub><b>Kevin Doocey</b></sub>](https://www.dooceykev.in)<br />[💻](https://github.com/netlify/netlify-cms/commits?author=Doocey "Code") | [<img src="https://avatars0.githubusercontent.com/u/31023010?v=4" width="100px;"/><br /><sub><b>Swieckowski</b></sub>](https://www.linkedin.com/in/arthur-swieckowski/)<br />[💻](https://github.com/netlify/netlify-cms/commits?author=Swieckowski "Code") [📖](https://github.com/netlify/netlify-cms/commits?author=Swieckowski "Documentation") [⚠️](https://github.com/netlify/netlify-cms/commits?author=Swieckowski "Tests") |
| [<img src="https://avatars0.githubusercontent.com/u/30510616?v=4" width="100px;"/><br /><sub><b>Sol Park</b></sub>](https://github.com/solpark)<br />[💻](https://github.com/netlify/netlify-cms/commits?author=solpark "Code") | [<img src="https://avatars0.githubusercontent.com/u/29218846?v=4" width="100px;"/><br /><sub><b>Michael Romani</b></sub>](https://github.com/michaelromani)<br />[💻](https://github.com/netlify/netlify-cms/commits?author=michaelromani "Code") | | [<img src="https://avatars2.githubusercontent.com/u/283419?v=4" width="100px;"/><br /><sub><b>Tim Carry</b></sub>](http://www.pixelastic.com/)<br />[💻](https://github.com/netlify/netlify-cms/commits?author=pixelastic "Code") [🎨](#design-pixelastic "Design") [📖](https://github.com/netlify/netlify-cms/commits?author=pixelastic "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/30510616?v=4" width="100px;"/><br /><sub><b>Sol Park</b></sub>](https://github.com/solpark)<br />[💻](https://github.com/netlify/netlify-cms/commits?author=solpark "Code") | [<img src="https://avatars0.githubusercontent.com/u/29218846?v=4" width="100px;"/><br /><sub><b>Michael Romani</b></sub>](https://github.com/michaelromani)<br />[💻](https://github.com/netlify/netlify-cms/commits?author=michaelromani "Code") |
<!-- ALL-CONTRIBUTORS-LIST:END --> <!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome! This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!

View File

@ -1,4 +1,4 @@
import { FrontmatterInfer, FrontmatterJSON, FrontmatterTOML, FrontmatterYAML } from '../frontmatter'; import { FrontmatterInfer, frontmatterJSON, frontmatterTOML, frontmatterYAML } from '../frontmatter';
jest.mock("../../valueObjects/AssetProxy.js"); jest.mock("../../valueObjects/AssetProxy.js");
@ -15,9 +15,21 @@ describe('Frontmatter', () => {
); );
}); });
it('should parse YAML with --- delimiters when it is explicitly set as the format', () => { it('should parse YAML with --- delimiters when it is explicitly set as the format without a custom delimiter', () => {
expect( expect(
FrontmatterYAML.fromFile('---\ntitle: YAML\ndescription: Something longer\n---\nContent') frontmatterYAML().fromFile('---\ntitle: YAML\ndescription: Something longer\n---\nContent')
).toEqual(
{
title: 'YAML',
description: 'Something longer',
body: 'Content',
}
);
});
it('should parse YAML with custom delimiters when it is explicitly set as the format with a custom delimiter', () => {
expect(
frontmatterYAML("~~~").fromFile('~~~\ntitle: YAML\ndescription: Something longer\n~~~\nContent')
).toEqual( ).toEqual(
{ {
title: 'YAML', title: 'YAML',
@ -62,9 +74,9 @@ describe('Frontmatter', () => {
); );
}); });
it('should parse TOML with +++ delimiters when it is explicitly set as the format', () => { it('should parse TOML with +++ delimiters when it is explicitly set as the format without a custom delimiter', () => {
expect( expect(
FrontmatterTOML.fromFile('+++\ntitle = "TOML"\ndescription = "Front matter"\n+++\nContent') frontmatterTOML("~~~").fromFile('~~~\ntitle = "TOML"\ndescription = "Front matter"\n~~~\nContent')
).toEqual( ).toEqual(
{ {
title: 'TOML', title: 'TOML',
@ -98,9 +110,21 @@ describe('Frontmatter', () => {
); );
}); });
it('should parse JSON with { } delimiters when it is explicitly set as the format', () => { it('should parse JSON with { } delimiters when it is explicitly set as the format without a custom delimiter', () => {
expect( expect(
FrontmatterJSON.fromFile('{\n"title": "The Title",\n"description": "Something longer"\n}\nContent') frontmatterJSON().fromFile('{\n"title": "The Title",\n"description": "Something longer"\n}\nContent')
).toEqual(
{
title: 'The Title',
description: 'Something longer',
body: 'Content',
}
);
});
it('should parse JSON with { } delimiters when it is explicitly set as the format with a custom delimiter', () => {
expect(
frontmatterJSON("~~~").fromFile('~~~\n"title": "The Title",\n"description": "Something longer"\n~~~\nContent')
).toEqual( ).toEqual(
{ {
title: 'The Title', title: 'The Title',
@ -156,9 +180,10 @@ describe('Frontmatter', () => {
); );
}); });
it('should stringify YAML with --- delimiters when it is explicitly set as the format', () => { it('should stringify YAML with --- delimiters when it is explicitly set as the format without a custom delimiter',
() => {
expect( expect(
FrontmatterYAML.toFile({ body: 'Some content\nOn another line', tags: ['front matter', 'yaml'], title: 'YAML' }) frontmatterYAML().toFile({ body: 'Some content\nOn another line', tags: ['front matter', 'yaml'], title: 'YAML' })
).toEqual( ).toEqual(
[ [
'---', '---',
@ -173,9 +198,28 @@ describe('Frontmatter', () => {
); );
}); });
it('should stringify TOML with +++ delimiters when it is explicitly set as the format', () => { it('should stringify YAML with --- delimiters when it is explicitly set as the format with a custom delimiter',
() => {
expect( expect(
FrontmatterTOML.toFile({ body: 'Some content\nOn another line', tags: ['front matter', 'toml'], title: 'TOML' }) frontmatterYAML("~~~").toFile({ body: 'Some content\nOn another line', tags: ['front matter', 'yaml'], title: 'YAML' })
).toEqual(
[
'~~~',
'tags:',
' - front matter',
' - yaml',
'title: YAML',
'~~~',
'Some content',
'On another line\n',
].join('\n')
);
});
it('should stringify TOML with +++ delimiters when it is explicitly set as the format without a custom delimiter',
() => {
expect(
frontmatterTOML().toFile({ body: 'Some content\nOn another line', tags: ['front matter', 'toml'], title: 'TOML' })
).toEqual( ).toEqual(
[ [
'+++', '+++',
@ -188,9 +232,26 @@ describe('Frontmatter', () => {
); );
}); });
it('should stringify JSON with { } delimiters when it is explicitly set as the format', () => { it('should stringify TOML with +++ delimiters when it is explicitly set as the format with a custom delimiter',
() => {
expect( expect(
FrontmatterJSON.toFile({ body: 'Some content\nOn another line', tags: ['front matter', 'json'], title: 'JSON' }) frontmatterTOML("~~~").toFile({ body: 'Some content\nOn another line', tags: ['front matter', 'toml'], title: 'TOML' })
).toEqual(
[
'~~~',
'tags = ["front matter", "toml"]',
'title = "TOML"',
'~~~',
'Some content',
'On another line\n',
].join('\n')
);
});
it('should stringify JSON with { } delimiters when it is explicitly set as the format without a custom delimiter',
() => {
expect(
frontmatterJSON().toFile({ body: 'Some content\nOn another line', tags: ['front matter', 'json'], title: 'JSON' })
).toEqual( ).toEqual(
[ [
'{', '{',
@ -205,4 +266,23 @@ describe('Frontmatter', () => {
].join('\n') ].join('\n')
); );
}); });
it('should stringify JSON with { } delimiters when it is explicitly set as the format with a custom delimiter',
() => {
expect(
frontmatterJSON("~~~").toFile({ body: 'Some content\nOn another line', tags: ['front matter', 'json'], title: 'JSON' })
).toEqual(
[
'~~~',
'"tags": [',
' "front matter",',
' "json"',
' ],',
' "title": "JSON"',
'~~~',
'Some content',
'On another line\n',
].join('\n')
);
});
}); });

View File

@ -1,7 +1,9 @@
import yamlFormatter from './yaml'; import yamlFormatter from './yaml';
import tomlFormatter from './toml'; import tomlFormatter from './toml';
import jsonFormatter from './json'; import jsonFormatter from './json';
import { FrontmatterInfer, FrontmatterJSON, FrontmatterTOML, FrontmatterYAML } from './frontmatter'; import { FrontmatterInfer, frontmatterJSON, frontmatterTOML, frontmatterYAML } from './frontmatter';
export const frontmatterFormats = ['yaml-frontmatter','toml-frontmatter','json-frontmatter']
export const supportedFormats = [ export const supportedFormats = [
'yml', 'yml',
@ -37,24 +39,26 @@ export function formatByExtension(extension) {
}[extension]; }[extension];
} }
function formatByName(name) { function formatByName(name, customDelimiter) {
return { return {
yml: yamlFormatter, yml: yamlFormatter,
yaml: yamlFormatter, yaml: yamlFormatter,
toml: tomlFormatter, toml: tomlFormatter,
json: jsonFormatter, json: jsonFormatter,
frontmatter: FrontmatterInfer, frontmatter: FrontmatterInfer,
'json-frontmatter': FrontmatterJSON, 'json-frontmatter': frontmatterJSON(customDelimiter),
'toml-frontmatter': FrontmatterTOML, 'toml-frontmatter': frontmatterTOML(customDelimiter),
'yaml-frontmatter': FrontmatterYAML, 'yaml-frontmatter': frontmatterYAML(customDelimiter),
}[name]; }[name];
} }
export function resolveFormat(collectionOrEntity, entry) { export function resolveFormat(collectionOrEntity, entry) {
// Check for custom delimiter
const customDelimiter = collectionOrEntity.get('frontmatter_delimiter');
// If the format is specified in the collection, use that format. // If the format is specified in the collection, use that format.
const format = collectionOrEntity.get('format'); const formatSpecification = collectionOrEntity.get('format');
if (format) { if (formatSpecification) {
return formatByName(format); return formatByName(formatSpecification, customDelimiter);
} }
// If a file already exists, infer the format from its file extension. // If a file already exists, infer the format from its file extension.
@ -72,5 +76,5 @@ export function resolveFormat(collectionOrEntity, entry) {
} }
// If no format is specified and it cannot be inferred, return the default. // If no format is specified and it cannot be inferred, return the default.
return formatByName('frontmatter'); return formatByName('frontmatter', customDelimiter);
} }

View File

@ -60,12 +60,14 @@ export const getFormatOpts = format => ({
}[format]); }[format]);
class FrontmatterFormatter { class FrontmatterFormatter {
constructor(format) { constructor(format, customDelimiter) {
this.format = getFormatOpts(format); this.format = getFormatOpts(format);
this.customDelimiter = customDelimiter;
} }
fromFile(content) { fromFile(content) {
const format = this.format || inferFrontmatterFormat(content); const format = this.format || inferFrontmatterFormat(content);
if (this.customDelimiter) this.format.delimiters = this.customDelimiter;
const result = matter(content, { engines: parsers, ...format }); const result = matter(content, { engines: parsers, ...format });
return { return {
...result.data, ...result.data,
@ -78,6 +80,7 @@ class FrontmatterFormatter {
// Stringify to YAML if the format was not set // Stringify to YAML if the format was not set
const format = this.format || getFormatOpts('yaml'); const format = this.format || getFormatOpts('yaml');
if (this.customDelimiter) this.format.delimiters = this.customDelimiter;
// `sortedKeys` is not recognized by gray-matter, so it gets passed through to the parser // `sortedKeys` is not recognized by gray-matter, so it gets passed through to the parser
return matter.stringify(body, meta, { engines: parsers, sortedKeys, ...format }); return matter.stringify(body, meta, { engines: parsers, sortedKeys, ...format });
@ -85,6 +88,6 @@ class FrontmatterFormatter {
} }
export const FrontmatterInfer = new FrontmatterFormatter(); export const FrontmatterInfer = new FrontmatterFormatter();
export const FrontmatterYAML = new FrontmatterFormatter('yaml'); export const frontmatterYAML = customDelimiter => new FrontmatterFormatter('yaml', customDelimiter);
export const FrontmatterTOML = new FrontmatterFormatter('toml'); export const frontmatterTOML = customDelimiter => new FrontmatterFormatter('toml', customDelimiter);
export const FrontmatterJSON = new FrontmatterFormatter('json'); export const frontmatterJSON = customDelimiter => new FrontmatterFormatter('json', customDelimiter);

View File

@ -4,7 +4,7 @@ import consoleError from 'Lib/consoleError';
import { CONFIG_SUCCESS } from 'Actions/config'; import { CONFIG_SUCCESS } from 'Actions/config';
import { FILES, FOLDER } from 'Constants/collectionTypes'; import { FILES, FOLDER } from 'Constants/collectionTypes';
import { INFERABLE_FIELDS } from 'Constants/fieldInference'; import { INFERABLE_FIELDS } from 'Constants/fieldInference';
import { formatByExtension, formatToExtension, supportedFormats } from 'Formats/formats'; import { formatByExtension, formatToExtension, supportedFormats, frontmatterFormats } from 'Formats/formats';
const collections = (state = null, action) => { const collections = (state = null, action) => {
const configCollections = action.payload && action.payload.collections; const configCollections = action.payload && action.payload.collections;
@ -38,6 +38,11 @@ function validateCollection(configCollection) {
// Cannot infer format from extension. // Cannot infer format from extension.
throw new Error(`Please set a format for collection "${ collectionName }". Supported formats are ${ supportedFormats.join(',') }`); throw new Error(`Please set a format for collection "${ collectionName }". Supported formats are ${ supportedFormats.join(',') }`);
} }
if (has(configCollection, 'frontmatter_delimiter') && !frontmatterFormats.includes(get(configCollection, 'format'))) {
// Cannot set custom delimiter without explicit and proper frontmatter format declaration
throw new Error(`Please set a proper frontmatter format for collection "${ collectionName }" to use a custom delimiter. Supported frontmatter formats are yaml-frontmatter, toml-frontmatter, and json-frontmatter.`);
}
} }
const selectors = { const selectors = {

View File

@ -77,6 +77,7 @@ The `collections` setting is the heart of your Netlify CMS configuration, as it
- `delete`: `false` prevents users from deleting items in a collection; defaults to `true` - `delete`: `false` prevents users from deleting items in a collection; defaults to `true`
- `extension`: see detailed description below - `extension`: see detailed description below
- `format`: see detailed description below - `format`: see detailed description below
- `frontmatter_delimiter`: see detailed description under `format`
- `slug`: see detailed description below - `slug`: see detailed description below
- `fields` (required): see detailed description below - `fields` (required): see detailed description below
@ -96,11 +97,11 @@ You may also specify a custom `extension` not included in the list above, as lon
- `toml`: parses and saves files as TOML-formatted data files; saves with `toml` extension by default - `toml`: parses and saves files as TOML-formatted data files; saves with `toml` extension by default
- `json`: parses and saves files as JSON-formatted data files; saves with `json` extension by default - `json`: parses and saves files as JSON-formatted data files; saves with `json` extension by default
- `frontmatter`: parses files and saves files with data frontmatter followed by an unparsed body text (edited using a `body` field); saves with `md` extension by default; default for collections that can't be inferred. Collections with `frontmatter` format (either inferred or explicitly set) can parse files with frontmatter in YAML, TOML, or JSON format. However, they will be saved with YAML frontmatter. - `frontmatter`: parses files and saves files with data frontmatter followed by an unparsed body text (edited using a `body` field); saves with `md` extension by default; default for collections that can't be inferred. Collections with `frontmatter` format (either inferred or explicitly set) can parse files with frontmatter in YAML, TOML, or JSON format. However, they will be saved with YAML frontmatter.
- `yaml-frontmatter`: same as the `frontmatter` format above, except frontmatter will be both parsed and saved only as YAML, followed by unparsed body text - `yaml-frontmatter`: same as the `frontmatter` format above, except frontmatter will be both parsed and saved only as YAML, followed by unparsed body text. The default delimiter for this option is `---`.
- `toml-frontmatter`: same as the `frontmatter` format above, except frontmatter will be both parsed and saved only as TOML, followed by unparsed body text - `toml-frontmatter`: same as the `frontmatter` format above, except frontmatter will be both parsed and saved only as TOML, followed by unparsed body text. The default delimiter for this option is `+++`.
- `json-frontmatter`: same as the `frontmatter` format above, except frontmatter will be both parsed and saved as JSON, followed by unparsed body text - `json-frontmatter`: same as the `frontmatter` format above, except frontmatter will be both parsed and saved as JSON, followed by unparsed body text. The default delimiter for this option is `{` `}`.
The explicit `yaml-frontmatter`, `toml-frontmatter`, and `json-frontmatter` formats above do not currently support custom delimiters. We use `---` for YAML, `+++` for TOML, and `{` `}` for JSON. If a file has frontmatter inside other delimiters it will be included as part of the body text. `frontmatter_delimiter`: if you have an explicit frontmatter format declared, this option allows you to specify a custom delimiter like `~~~`.
### `slug` ### `slug`