diff --git a/packages/netlify-cms-core/src/actions/__tests__/config.spec.js b/packages/netlify-cms-core/src/actions/__tests__/config.spec.js index baff41c0..777e1c96 100644 --- a/packages/netlify-cms-core/src/actions/__tests__/config.spec.js +++ b/packages/netlify-cms-core/src/actions/__tests__/config.spec.js @@ -1,5 +1,6 @@ import { fromJS } from 'immutable'; -import { applyDefaults, detectProxyServer, handleLocalBackend } from '../config'; +import { stripIndent } from 'common-tags'; +import { parseConfig, applyDefaults, detectProxyServer, handleLocalBackend } from '../config'; jest.spyOn(console, 'log').mockImplementation(() => {}); jest.mock('coreSrc/backend', () => { @@ -9,6 +10,82 @@ jest.mock('coreSrc/backend', () => { }); describe('config', () => { + describe('parseConfig', () => { + it('can parse simple yaml config', () => { + const file = stripIndent` + backend: + name: test-repo + media_folder: static/images + `; + + expect(parseConfig(file)).toEqual({ + backend: { name: 'test-repo' }, + media_folder: 'static/images', + }); + }); + + it('should merge yaml aliases', () => { + const file = stripIndent` + backend: + name: github + repo: netlify/netlify-cms + squash_merges: true + open_authoring: true + local_backend: true + site_url: https://www.netlifycms.org + publish_mode: editorial_workflow + media_folder: website/static/img + public_folder: img + docs_collection: &docs_collection + folder: website/content/docs + create: true + preview_path: 'docs/{{slug}}' + fields: + - { label: Title, name: title } + - { label: Body, name: body, widget: markdown } + collections: + - <<: *docs_collection + name: docs_start + label: 'Docs: Quick Start' + `; + + expect(parseConfig(file)).toEqual({ + backend: { + name: 'github', + repo: 'netlify/netlify-cms', + squash_merges: true, + open_authoring: true, + }, + local_backend: true, + site_url: 'https://www.netlifycms.org', + publish_mode: 'editorial_workflow', + media_folder: 'website/static/img', + public_folder: 'img', + docs_collection: { + folder: 'website/content/docs', + create: true, + preview_path: 'docs/{{slug}}', + fields: [ + { label: 'Title', name: 'title' }, + { label: 'Body', name: 'body', widget: 'markdown' }, + ], + }, + collections: [ + { + folder: 'website/content/docs', + create: true, + preview_path: 'docs/{{slug}}', + fields: [ + { label: 'Title', name: 'title' }, + { label: 'Body', name: 'body', widget: 'markdown' }, + ], + name: 'docs_start', + label: 'Docs: Quick Start', + }, + ], + }); + }); + }); describe('applyDefaults', () => { describe('publish_mode', () => { it('should set publish_mode if not set', () => { diff --git a/packages/netlify-cms-core/src/actions/config.js b/packages/netlify-cms-core/src/actions/config.js index 4563c25a..37d715f2 100644 --- a/packages/netlify-cms-core/src/actions/config.js +++ b/packages/netlify-cms-core/src/actions/config.js @@ -118,7 +118,7 @@ function mergePreloadedConfig(preloadedConfig, loadedConfig) { return preloadedConfig ? preloadedConfig.mergeDeep(map) : map; } -function parseConfig(data) { +export function parseConfig(data) { const config = yaml.parse(data, { maxAliasCount: -1, prettyErrors: true, merge: true }); if (typeof CMS_ENV === 'string' && config[CMS_ENV]) { Object.keys(config[CMS_ENV]).forEach(key => { 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 eef5d7a8..3a5d9a58 100644 --- a/packages/netlify-cms-core/src/formats/__tests__/frontmatter.spec.js +++ b/packages/netlify-cms-core/src/formats/__tests__/frontmatter.spec.js @@ -6,348 +6,412 @@ import { } from '../frontmatter'; describe('Frontmatter', () => { - it('should parse YAML with --- delimiters', () => { - expect( - FrontmatterInfer.fromFile('---\ntitle: YAML\ndescription: Something longer\n---\nContent'), - ).toEqual({ - title: 'YAML', - description: 'Something longer', - body: 'Content', - }); - }); - - 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'), - ).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', - description: 'Something longer', - body: 'Content', - }); - }); - - it('should parse YAML with custom delimiters when it is explicitly set as the format with different custom delimiters', () => { - expect( - frontmatterYAML(['~~~', '^^^']).fromFile( - '~~~\ntitle: YAML\ndescription: Something longer\n^^^\nContent', - ), - ).toEqual({ - title: 'YAML', - description: 'Something longer', - body: 'Content', - }); - }); - - it('should parse YAML with ---yaml delimiters', () => { - expect( - FrontmatterInfer.fromFile( - '---yaml\ntitle: YAML\ndescription: Something longer\n---\nContent', - ), - ).toEqual({ - title: 'YAML', - description: 'Something longer', - body: 'Content', - }); - }); - - it('should overwrite any body param in the front matter', () => { - expect( - FrontmatterInfer.fromFile('---\ntitle: The Title\nbody: Something longer\n---\nContent'), - ).toEqual({ - title: 'The Title', - body: 'Content', - }); - }); - - it('should parse TOML with +++ delimiters', () => { - expect( - FrontmatterInfer.fromFile('+++\ntitle = "TOML"\ndescription = "Front matter"\n+++\nContent'), - ).toEqual({ - title: 'TOML', - description: 'Front matter', - body: 'Content', - }); - }); - - it('should parse TOML with 0.5 style dates', () => { - expect( - FrontmatterInfer.fromFile('+++\ntitle = "TOML"\ndate = 2018-12-24\n+++\nContent'), - ).toEqual({ - title: 'TOML', - date: new Date('2018-12-24T00:00:00.000Z'), - body: 'Content', - }); - }); - - 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', - ), - ).toEqual({ - title: 'TOML', - description: 'Front matter', - body: 'Content', - }); - }); - - it('should parse TOML with ---toml delimiters', () => { - expect( - FrontmatterInfer.fromFile( - '---toml\ntitle = "TOML"\ndescription = "Something longer"\n---\nContent', - ), - ).toEqual({ - title: 'TOML', - description: 'Something longer', - body: 'Content', - }); - }); - - it('should parse JSON with { } delimiters', () => { - expect( - FrontmatterInfer.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 without a custom delimiter', () => { - expect( - 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', - description: 'Something longer', - body: 'Content', - }); - }); - - it('should parse JSON with ---json delimiters', () => { - expect( - FrontmatterInfer.fromFile( - '---json\n{\n"title": "The Title",\n"description": "Something longer"\n}\n---\nContent', - ), - ).toEqual({ - title: 'The Title', - description: 'Something longer', - body: 'Content', - }); - }); - - it('should stringify YAML with --- delimiters', () => { - expect( - FrontmatterInfer.toFile({ - body: 'Some content\nOn another line', - tags: ['front matter', 'yaml'], + describe('yaml', () => { + it('should parse YAML with --- delimiters', () => { + expect( + FrontmatterInfer.fromFile('---\ntitle: YAML\ndescription: Something longer\n---\nContent'), + ).toEqual({ title: 'YAML', - }), - ).toEqual( - [ - '---', - 'tags:', - ' - front matter', - ' - yaml', - 'title: YAML', - '---', - 'Some content', - 'On another line', - ].join('\n'), - ); - }); + description: 'Something longer', + body: 'Content', + }); + }); - 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'), - ); - }); - - 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'], + 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'), + ).toEqual({ title: 'YAML', - }), - ).toEqual( - [ - '---', - 'tags:', - ' - front matter', - ' - yaml', - 'title: YAML', - '---', - 'Some content', - 'On another line', - ].join('\n'), - ); - }); + description: 'Something longer', + body: 'Content', + }); + }); - 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'], + 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', - }), - ).toEqual( - [ - '~~~', - 'tags:', - ' - front matter', - ' - yaml', - 'title: YAML', - '~~~', - 'Some content', - 'On another line', - ].join('\n'), - ); - }); + description: 'Something longer', + body: 'Content', + }); + }); - it('should stringify YAML with --- delimiters when it is explicitly set as the format with different custom delimiters', () => { - expect( - frontmatterYAML(['~~~', '^^^']).toFile({ - body: 'Some content\nOn another line', - tags: ['front matter', 'yaml'], + it('should parse YAML with custom delimiters when it is explicitly set as the format with different custom delimiters', () => { + expect( + frontmatterYAML(['~~~', '^^^']).fromFile( + '~~~\ntitle: YAML\ndescription: Something longer\n^^^\nContent', + ), + ).toEqual({ title: 'YAML', - }), - ).toEqual( - [ - '~~~', - 'tags:', - ' - front matter', - ' - yaml', - 'title: YAML', - '^^^', - 'Some content', - 'On another line', - ].join('\n'), - ); + description: 'Something longer', + body: 'Content', + }); + }); + + it('should parse YAML with ---yaml delimiters', () => { + expect( + FrontmatterInfer.fromFile( + '---yaml\ntitle: YAML\ndescription: Something longer\n---\nContent', + ), + ).toEqual({ + title: 'YAML', + description: 'Something longer', + body: 'Content', + }); + }); + + it('should overwrite any body param in the front matter', () => { + expect( + FrontmatterInfer.fromFile('---\ntitle: The Title\nbody: Something longer\n---\nContent'), + ).toEqual({ + title: 'The Title', + body: 'Content', + }); + }); + + it('should stringify YAML with --- delimiters', () => { + expect( + FrontmatterInfer.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', + ].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'), + ); + }); + + 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', + }), + ).toEqual( + [ + '---', + 'tags:', + ' - front matter', + ' - yaml', + 'title: YAML', + '---', + 'Some content', + 'On another line', + ].join('\n'), + ); + }); + + 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', + ].join('\n'), + ); + }); + + it('should stringify YAML with --- delimiters when it is explicitly set as the format with different custom delimiters', () => { + 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', + ].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'); + }); + + it('should keep field types', () => { + const frontmatter = frontmatterYAML(); + const file = frontmatter.toFile({ + number: 1, + string: 'Hello World!', + date: new Date('2020-01-01'), + array: ['1', new Date('2020-01-01')], + body: 'Content', + }); + expect(frontmatter.fromFile(file)).toEqual({ + number: 1, + string: 'Hello World!', + date: new Date('2020-01-01'), + array: ['1', new Date('2020-01-01')], + body: 'Content', + }); + }); }); - 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'], + describe('toml', () => { + it('should parse TOML with +++ delimiters', () => { + expect( + FrontmatterInfer.fromFile( + '+++\ntitle = "TOML"\ndescription = "Front matter"\n+++\nContent', + ), + ).toEqual({ title: 'TOML', - }), - ).toEqual( - [ - '+++', - 'tags = ["front matter", "toml"]', - 'title = "TOML"', - '+++', - 'Some content', - 'On another line', - ].join('\n'), - ); - }); + description: 'Front matter', + body: 'Content', + }); + }); - 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'], + it('should parse TOML with 0.5 style dates', () => { + expect( + FrontmatterInfer.fromFile('+++\ntitle = "TOML"\ndate = 2018-12-24\n+++\nContent'), + ).toEqual({ title: 'TOML', - }), - ).toEqual( - [ - '~~~', - 'tags = ["front matter", "toml"]', - 'title = "TOML"', - '~~~', - 'Some content', - 'On another line', - ].join('\n'), - ); + date: new Date('2018-12-24T00:00:00.000Z'), + body: 'Content', + }); + }); + + 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', + ), + ).toEqual({ + title: 'TOML', + description: 'Front matter', + body: 'Content', + }); + }); + + it('should parse TOML with ---toml delimiters', () => { + expect( + FrontmatterInfer.fromFile( + '---toml\ntitle = "TOML"\ndescription = "Something longer"\n---\nContent', + ), + ).toEqual({ + title: 'TOML', + description: 'Something longer', + body: 'Content', + }); + }); + + 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( + [ + '+++', + 'tags = ["front matter", "toml"]', + 'title = "TOML"', + '+++', + 'Some content', + 'On another line', + ].join('\n'), + ); + }); + + 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', + ].join('\n'), + ); + }); + + it('should keep field types', () => { + const frontmatter = frontmatterTOML(); + const file = frontmatter.toFile({ + number: 1, + string: 'Hello World!', + date: new Date('2020-01-01'), + // in toml arrays must contain the same type + array: ['1', new Date('2020-01-01').toISOString()], + body: 'Content', + }); + expect(frontmatter.fromFile(file)).toEqual({ + number: 1, + string: 'Hello World!', + date: new Date('2020-01-01'), + array: ['1', new Date('2020-01-01').toISOString()], + body: 'Content', + }); + }); }); - 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( - [ - '{', - '"tags": [', - ' "front matter",', - ' "json"', - ' ],', - ' "title": "JSON"', - '}', - 'Some content', - 'On another line', - ].join('\n'), - ); - }); + describe('json', () => { + it('should parse JSON with { } delimiters', () => { + expect( + FrontmatterInfer.fromFile( + '{\n"title": "The Title",\n"description": "Something longer"\n}\nContent', + ), + ).toEqual({ + title: 'The Title', + description: 'Something longer', + body: 'Content', + }); + }); - 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', - ].join('\n'), - ); - }); + 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', + ), + ).toEqual({ + title: 'The Title', + description: 'Something longer', + body: 'Content', + }); + }); - it('should trim last line break if added by grey-matter', () => { - expect( - frontmatterYAML().toFile({ - body: 'noLineBreak', - }), - ).toEqual('noLineBreak'); - }); + 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', + description: 'Something longer', + body: 'Content', + }); + }); - it('should not trim last line break if not added by grey-matter', () => { - expect( - frontmatterYAML().toFile({ - body: 'withLineBreak\n', - }), - ).toEqual('withLineBreak\n'); + it('should parse JSON with ---json delimiters', () => { + expect( + FrontmatterInfer.fromFile( + '---json\n{\n"title": "The Title",\n"description": "Something longer"\n}\n---\nContent', + ), + ).toEqual({ + title: 'The Title', + description: 'Something longer', + body: 'Content', + }); + }); + + 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( + [ + '{', + '"tags": [', + ' "front matter",', + ' "json"', + ' ],', + ' "title": "JSON"', + '}', + 'Some content', + 'On another line', + ].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', + ].join('\n'), + ); + }); + + it('should keep field types', () => { + const frontmatter = frontmatterJSON(); + const file = frontmatter.toFile({ + number: 1, + string: 'Hello World!', + // no way to represent date in JSON + date: new Date('2020-01-01').toISOString(), + array: ['1', new Date('2020-01-01').toISOString()], + body: 'Content', + }); + expect(frontmatter.fromFile(file)).toEqual({ + number: 1, + string: 'Hello World!', + date: new Date('2020-01-01').toISOString(), + array: ['1', new Date('2020-01-01').toISOString()], + body: 'Content', + }); + }); }); }); diff --git a/packages/netlify-cms-core/src/formats/__tests__/tomlFormatter.spec.js b/packages/netlify-cms-core/src/formats/__tests__/toml.spec.js similarity index 100% rename from packages/netlify-cms-core/src/formats/__tests__/tomlFormatter.spec.js rename to packages/netlify-cms-core/src/formats/__tests__/toml.spec.js