diff --git a/.all-contributorsrc b/.all-contributorsrc
index 4902df2aaa56..1ff144fab29e 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -710,6 +710,17 @@
"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",
"name": "Tim Carry",
diff --git a/README.md b/README.md
index 1e37c35403ba..6ace4e1ea8ec 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,5 @@
# Netlify CMS
-
-[![All Contributors](https://img.shields.io/badge/all_contributors-86-orange.svg)](#contributors)
+[![All Contributors](https://img.shields.io/badge/all_contributors-87-orange.svg)](#contributors)
[![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)
@@ -62,8 +61,8 @@ Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds
| [
Etienne Bernard](https://github.com/ebernard)
[📖](https://github.com/netlify/netlify-cms/commits?author=ebernard "Documentation") | [
Eli Williamson](http://eliwilliamson.com)
[💻](https://github.com/netlify/netlify-cms/commits?author=eliwilliamson "Code") [🎨](#design-eliwilliamson "Design") | [
Covington Doan](https://www.covingtondoan.com)
[📖](https://github.com/netlify/netlify-cms/commits?author=covingtondoan "Documentation") | [
Lennart Ziburski](http://lennartziburski.com)
[💻](https://github.com/netlify/netlify-cms/commits?author=ziburski "Code") [🎨](#design-ziburski "Design") [📖](https://github.com/netlify/netlify-cms/commits?author=ziburski "Documentation") | [
Darrel O'Pry](http://darrelopry.com)
[💻](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") | [
Erin Symons](https://github.com/yourfrienderin)
[📖](https://github.com/netlify/netlify-cms/commits?author=yourfrienderin "Documentation") | [
Austin Green](https://github.com/AustinGreen)
[📖](https://github.com/netlify/netlify-cms/commits?author=AustinGreen "Documentation") [💡](#example-AustinGreen "Examples") |
| [
Bryan Robinson](http://bryanlrobinson.com)
[📖](https://github.com/netlify/netlify-cms/commits?author=brob "Documentation") | [
Darren](https://github.com/dardub)
[📖](https://github.com/netlify/netlify-cms/commits?author=dardub "Documentation") | [
Richard Pullinger](http://www.richardpullinger.com)
[💻](https://github.com/netlify/netlify-cms/commits?author=rpullinger "Code") | [
Taylor Bryant](https://taylorbryant.blog)
[📖](https://github.com/netlify/netlify-cms/commits?author=taylorbryant "Documentation") | [
kvanerkelens](https://github.com/kvanerkelens)
[📖](https://github.com/netlify/netlify-cms/commits?author=kvanerkelens "Documentation") | [
Patrick Sier](https://patsier.com/)
[💻](https://github.com/netlify/netlify-cms/commits?author=pjsier "Code") | [
Christian Nolte](http://noltech.net)
[💻](https://github.com/netlify/netlify-cms/commits?author=drlogout "Code") |
| [
Edward Betts](http://edwardbetts.com/)
[📖](https://github.com/netlify/netlify-cms/commits?author=EdwardBetts "Documentation") | [
Josh Hardman](https://github.com/jhardman0830)
[📖](https://github.com/netlify/netlify-cms/commits?author=jhardman0830 "Documentation") | [
Mantas](https://behance.net/mistermantas)
[📖](https://github.com/netlify/netlify-cms/commits?author=mistermantas "Documentation") | [
Marco Biedermann](https://www.marcobiedermann.com)
[📖](https://github.com/netlify/netlify-cms/commits?author=marcobiedermann "Documentation") | [
Max Stoiber](https://mxstbr.blog/newsletter)
[📖](https://github.com/netlify/netlify-cms/commits?author=mxstbr "Documentation") | [
Vyacheslav Matyukhin](http://berekuk.ru)
[📖](https://github.com/netlify/netlify-cms/commits?author=berekuk "Documentation") | [
jimmaaay](https://jimmythompson.me)
[💻](https://github.com/netlify/netlify-cms/commits?author=jimmaaay "Code") |
-| [
Luís Miguel](https://github.com/Quicksaver)
[🐛](https://github.com/netlify/netlify-cms/issues?q=author%3AQuicksaver "Bug reports") [💻](https://github.com/netlify/netlify-cms/commits?author=Quicksaver "Code") | [
Chris Swithinbank](http://chrisswithinbank.net/)
[📖](https://github.com/netlify/netlify-cms/commits?author=delucis "Documentation") | [
remmah](https://github.com/remmah)
[📖](https://github.com/netlify/netlify-cms/commits?author=remmah "Documentation") | [
Sumeet Jain](http://sumeetjain.com)
[📖](https://github.com/netlify/netlify-cms/commits?author=sumeetjain "Documentation") | [
Sagar Khatri](https://github.com/ragasirtahk)
[📖](https://github.com/netlify/netlify-cms/commits?author=ragasirtahk "Documentation") [💡](#example-ragasirtahk "Examples") | [
Kevin Doocey](https://www.dooceykev.in)
[💻](https://github.com/netlify/netlify-cms/commits?author=Doocey "Code") | [
Tim Carry](http://www.pixelastic.com/)
[💻](https://github.com/netlify/netlify-cms/commits?author=pixelastic "Code") [🎨](#design-pixelastic "Design") [📖](https://github.com/netlify/netlify-cms/commits?author=pixelastic "Documentation") |
-| [
Sol Park](https://github.com/solpark)
[💻](https://github.com/netlify/netlify-cms/commits?author=solpark "Code") | [
Michael Romani](https://github.com/michaelromani)
[💻](https://github.com/netlify/netlify-cms/commits?author=michaelromani "Code") |
+| [
Luís Miguel](https://github.com/Quicksaver)
[🐛](https://github.com/netlify/netlify-cms/issues?q=author%3AQuicksaver "Bug reports") [💻](https://github.com/netlify/netlify-cms/commits?author=Quicksaver "Code") | [
Chris Swithinbank](http://chrisswithinbank.net/)
[📖](https://github.com/netlify/netlify-cms/commits?author=delucis "Documentation") | [
remmah](https://github.com/remmah)
[📖](https://github.com/netlify/netlify-cms/commits?author=remmah "Documentation") | [
Sumeet Jain](http://sumeetjain.com)
[📖](https://github.com/netlify/netlify-cms/commits?author=sumeetjain "Documentation") | [
Sagar Khatri](https://github.com/ragasirtahk)
[📖](https://github.com/netlify/netlify-cms/commits?author=ragasirtahk "Documentation") [💡](#example-ragasirtahk "Examples") | [
Kevin Doocey](https://www.dooceykev.in)
[💻](https://github.com/netlify/netlify-cms/commits?author=Doocey "Code") | [
Swieckowski](https://www.linkedin.com/in/arthur-swieckowski/)
[💻](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") |
+| [
Tim Carry](http://www.pixelastic.com/)
[💻](https://github.com/netlify/netlify-cms/commits?author=pixelastic "Code") [🎨](#design-pixelastic "Design") [📖](https://github.com/netlify/netlify-cms/commits?author=pixelastic "Documentation") | [
Sol Park](https://github.com/solpark)
[💻](https://github.com/netlify/netlify-cms/commits?author=solpark "Code") | [
Michael Romani](https://github.com/michaelromani)
[💻](https://github.com/netlify/netlify-cms/commits?author=michaelromani "Code") |
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
diff --git a/src/formats/__tests__/frontmatter.spec.js b/src/formats/__tests__/frontmatter.spec.js
index 33cc41f7c29c..cdf41ba3af6d 100644
--- a/src/formats/__tests__/frontmatter.spec.js
+++ b/src/formats/__tests__/frontmatter.spec.js
@@ -1,4 +1,4 @@
-import { FrontmatterInfer, FrontmatterJSON, FrontmatterTOML, FrontmatterYAML } from '../frontmatter';
+import { FrontmatterInfer, frontmatterJSON, frontmatterTOML, frontmatterYAML } from '../frontmatter';
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(
- 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(
{
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(
- FrontmatterTOML.fromFile('+++\ntitle = "TOML"\ndescription = "Front matter"\n+++\nContent')
+ frontmatterTOML("~~~").fromFile('~~~\ntitle = "TOML"\ndescription = "Front matter"\n~~~\nContent')
).toEqual(
{
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(
- 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(
{
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(
- 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(
[
'---',
@@ -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(
+ 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' })
+ frontmatterTOML().toFile({ body: 'Some content\nOn another line', tags: ['front matter', 'toml'], title: 'TOML' })
).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(
+ 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' })
+ frontmatterJSON().toFile({ body: 'Some content\nOn another line', tags: ['front matter', 'json'], title: 'JSON' })
).toEqual(
[
'{',
@@ -205,4 +266,23 @@ describe('Frontmatter', () => {
].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')
+ );
+ });
});
diff --git a/src/formats/formats.js b/src/formats/formats.js
index a686364748b6..38be81e89fc8 100644
--- a/src/formats/formats.js
+++ b/src/formats/formats.js
@@ -1,7 +1,9 @@
import yamlFormatter from './yaml';
import tomlFormatter from './toml';
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 = [
'yml',
@@ -37,24 +39,26 @@ export function formatByExtension(extension) {
}[extension];
}
-function formatByName(name) {
+function formatByName(name, customDelimiter) {
return {
yml: yamlFormatter,
yaml: yamlFormatter,
toml: tomlFormatter,
json: jsonFormatter,
frontmatter: FrontmatterInfer,
- 'json-frontmatter': FrontmatterJSON,
- 'toml-frontmatter': FrontmatterTOML,
- 'yaml-frontmatter': FrontmatterYAML,
+ 'json-frontmatter': frontmatterJSON(customDelimiter),
+ 'toml-frontmatter': frontmatterTOML(customDelimiter),
+ 'yaml-frontmatter': frontmatterYAML(customDelimiter),
}[name];
}
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.
- const format = collectionOrEntity.get('format');
- if (format) {
- return formatByName(format);
+ const formatSpecification = collectionOrEntity.get('format');
+ if (formatSpecification) {
+ return formatByName(formatSpecification, customDelimiter);
}
// 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.
- return formatByName('frontmatter');
+ return formatByName('frontmatter', customDelimiter);
}
diff --git a/src/formats/frontmatter.js b/src/formats/frontmatter.js
index d6f1bb03d53d..804d9ac63f77 100644
--- a/src/formats/frontmatter.js
+++ b/src/formats/frontmatter.js
@@ -60,12 +60,14 @@ export const getFormatOpts = format => ({
}[format]);
class FrontmatterFormatter {
- constructor(format) {
+ constructor(format, customDelimiter) {
this.format = getFormatOpts(format);
+ this.customDelimiter = customDelimiter;
}
fromFile(content) {
const format = this.format || inferFrontmatterFormat(content);
+ if (this.customDelimiter) this.format.delimiters = this.customDelimiter;
const result = matter(content, { engines: parsers, ...format });
return {
...result.data,
@@ -78,6 +80,7 @@ class FrontmatterFormatter {
// Stringify to YAML if the format was not set
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
return matter.stringify(body, meta, { engines: parsers, sortedKeys, ...format });
@@ -85,6 +88,6 @@ class FrontmatterFormatter {
}
export const FrontmatterInfer = new FrontmatterFormatter();
-export const FrontmatterYAML = new FrontmatterFormatter('yaml');
-export const FrontmatterTOML = new FrontmatterFormatter('toml');
-export const FrontmatterJSON = new FrontmatterFormatter('json');
+export const frontmatterYAML = customDelimiter => new FrontmatterFormatter('yaml', customDelimiter);
+export const frontmatterTOML = customDelimiter => new FrontmatterFormatter('toml', customDelimiter);
+export const frontmatterJSON = customDelimiter => new FrontmatterFormatter('json', customDelimiter);
diff --git a/src/reducers/collections.js b/src/reducers/collections.js
index 78a25e3410e5..bddf78a713e0 100644
--- a/src/reducers/collections.js
+++ b/src/reducers/collections.js
@@ -4,7 +4,7 @@ import consoleError from 'Lib/consoleError';
import { CONFIG_SUCCESS } from 'Actions/config';
import { FILES, FOLDER } from 'Constants/collectionTypes';
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 configCollections = action.payload && action.payload.collections;
@@ -38,6 +38,11 @@ function validateCollection(configCollection) {
// Cannot infer format from extension.
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 = {
diff --git a/website/site/content/docs/configuration-options.md b/website/site/content/docs/configuration-options.md
index 1784d9cffc73..d744c27b4b5a 100644
--- a/website/site/content/docs/configuration-options.md
+++ b/website/site/content/docs/configuration-options.md
@@ -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`
- `extension`: see detailed description below
- `format`: see detailed description below
+- `frontmatter_delimiter`: see detailed description under `format`
- `slug`: 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
- `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.
-- `yaml-frontmatter`: same as the `frontmatter` format above, except frontmatter will be both parsed and saved only as YAML, 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
-- `json-frontmatter`: same as the `frontmatter` format above, except frontmatter will be both parsed and saved as JSON, 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. 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. 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`