This commit is contained in:
87
node_modules/decap-cms-core/src/formats/__tests__/formats.spec.js
generated
vendored
Normal file
87
node_modules/decap-cms-core/src/formats/__tests__/formats.spec.js
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
import { Map } from 'immutable';
|
||||
|
||||
import { extensionFormatters, resolveFormat } from '../formats';
|
||||
import { registerCustomFormat } from '../../lib/registry';
|
||||
|
||||
describe('custom formats', () => {
|
||||
const testEntry = {
|
||||
collection: 'testCollection',
|
||||
data: { x: 1 },
|
||||
isModification: false,
|
||||
label: 'testLabel',
|
||||
mediaFiles: [],
|
||||
meta: {},
|
||||
newRecord: true,
|
||||
partial: false,
|
||||
path: 'testPath1',
|
||||
raw: 'testRaw',
|
||||
slug: 'testSlug',
|
||||
author: 'testAuthor',
|
||||
updatedOn: 'testUpdatedOn',
|
||||
};
|
||||
it('resolves builtint formats', () => {
|
||||
const collection = Map({
|
||||
name: 'posts',
|
||||
});
|
||||
expect(resolveFormat(collection, { ...testEntry, path: 'test.yml' })).toEqual(
|
||||
extensionFormatters.yml,
|
||||
);
|
||||
expect(resolveFormat(collection, { ...testEntry, path: 'test.yaml' })).toEqual(
|
||||
extensionFormatters.yml,
|
||||
);
|
||||
expect(resolveFormat(collection, { ...testEntry, path: 'test.toml' })).toEqual(
|
||||
extensionFormatters.toml,
|
||||
);
|
||||
expect(resolveFormat(collection, { ...testEntry, path: 'test.json' })).toEqual(
|
||||
extensionFormatters.json,
|
||||
);
|
||||
expect(resolveFormat(collection, { ...testEntry, path: 'test.md' })).toEqual(
|
||||
extensionFormatters.md,
|
||||
);
|
||||
expect(resolveFormat(collection, { ...testEntry, path: 'test.markdown' })).toEqual(
|
||||
extensionFormatters.markdown,
|
||||
);
|
||||
expect(resolveFormat(collection, { ...testEntry, path: 'test.html' })).toEqual(
|
||||
extensionFormatters.html,
|
||||
);
|
||||
});
|
||||
|
||||
it('resolves custom format', () => {
|
||||
registerCustomFormat('txt-querystring', 'txt', {
|
||||
fromFile: file => Object.fromEntries(new URLSearchParams(file)),
|
||||
toFile: value => new URLSearchParams(value).toString(),
|
||||
});
|
||||
|
||||
const collection = Map({
|
||||
name: 'posts',
|
||||
format: 'txt-querystring',
|
||||
});
|
||||
|
||||
const formatter = resolveFormat(collection, { ...testEntry, path: 'test.txt' });
|
||||
|
||||
expect(formatter.toFile({ foo: 'bar' })).toEqual('foo=bar');
|
||||
expect(formatter.fromFile('foo=bar')).toEqual({ foo: 'bar' });
|
||||
});
|
||||
|
||||
it('can override existing formatters', () => {
|
||||
// simplified version of a more realistic use case: using a different yaml library like js-yaml
|
||||
// to make netlify-cms play nice with other tools that edit content and spit out yaml
|
||||
registerCustomFormat('bad-yaml', 'yml', {
|
||||
fromFile: file => Object.fromEntries(file.split('\n').map(line => line.split(': '))),
|
||||
toFile: value =>
|
||||
Object.entries(value)
|
||||
.map(([k, v]) => `${k}: ${v}`)
|
||||
.join('\n'),
|
||||
});
|
||||
|
||||
const collection = Map({
|
||||
name: 'posts',
|
||||
format: 'bad-yaml',
|
||||
});
|
||||
|
||||
const formatter = resolveFormat(collection, { ...testEntry, path: 'test.txt' });
|
||||
|
||||
expect(formatter.toFile({ a: 'b', c: 'd' })).toEqual('a: b\nc: d');
|
||||
expect(formatter.fromFile('a: b\nc: d')).toEqual({ a: 'b', c: 'd' });
|
||||
});
|
||||
});
|
||||
429
node_modules/decap-cms-core/src/formats/__tests__/frontmatter.spec.js
generated
vendored
Normal file
429
node_modules/decap-cms-core/src/formats/__tests__/frontmatter.spec.js
generated
vendored
Normal file
@@ -0,0 +1,429 @@
|
||||
import {
|
||||
FrontmatterInfer,
|
||||
frontmatterJSON,
|
||||
frontmatterTOML,
|
||||
frontmatterYAML,
|
||||
} from '../frontmatter';
|
||||
|
||||
describe('Frontmatter', () => {
|
||||
describe('yaml', () => {
|
||||
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 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',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('toml', () => {
|
||||
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 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',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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 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 parse JSON with { } delimiters ending with a nested object', () => {
|
||||
expect(
|
||||
FrontmatterInfer.fromFile(
|
||||
'{\n "title": "The Title",\n "nested": {\n "inside": "Inside prop"\n }\n}\nContent',
|
||||
),
|
||||
).toEqual({
|
||||
title: 'The Title',
|
||||
nested: { inside: 'Inside prop' },
|
||||
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',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
9
node_modules/decap-cms-core/src/formats/__tests__/toml.spec.js
generated
vendored
Normal file
9
node_modules/decap-cms-core/src/formats/__tests__/toml.spec.js
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import tomlFormatter from '../toml';
|
||||
|
||||
describe('tomlFormatter', () => {
|
||||
it('should output TOML integer values without decimals', () => {
|
||||
expect(tomlFormatter.toFile({ testFloat: 123.456, testInteger: 789, title: 'TOML' })).toEqual(
|
||||
['testFloat = 123.456', 'testInteger = 789', 'title = "TOML"'].join('\n'),
|
||||
);
|
||||
});
|
||||
});
|
||||
162
node_modules/decap-cms-core/src/formats/__tests__/yaml.spec.js
generated
vendored
Normal file
162
node_modules/decap-cms-core/src/formats/__tests__/yaml.spec.js
generated
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
import { stripIndent } from 'common-tags';
|
||||
|
||||
import yaml from '../yaml';
|
||||
|
||||
describe('yaml', () => {
|
||||
describe('fromFile', () => {
|
||||
test('loads valid yaml', () => {
|
||||
expect(yaml.fromFile('[]')).toEqual([]);
|
||||
|
||||
const result = yaml.fromFile(stripIndent`
|
||||
date: 2020-04-02T16:08:03.327Z
|
||||
dateString: 2020-04-02
|
||||
boolean: true
|
||||
number: 1
|
||||
`);
|
||||
expect(result).toEqual({
|
||||
date: new Date('2020-04-02T16:08:03.327Z'),
|
||||
dateString: '2020-04-02',
|
||||
boolean: true,
|
||||
number: 1,
|
||||
});
|
||||
expect(yaml.fromFile('# Comment a\na: a\nb:\n # Comment c\n c:\n d: d\n')).toEqual({
|
||||
a: 'a',
|
||||
b: { c: { d: 'd' } },
|
||||
});
|
||||
expect(
|
||||
yaml.fromFile(stripIndent`
|
||||
# template comment
|
||||
template: post
|
||||
# title comment
|
||||
title: title
|
||||
# image comment
|
||||
image: /media/netlify.png
|
||||
# date comment
|
||||
date: 2020-04-02T13:27:48.617Z
|
||||
# object comment
|
||||
object:
|
||||
# object_title comment
|
||||
object_title: object_title
|
||||
# object_list comment
|
||||
object_list:
|
||||
- object_list_item_1: "1"
|
||||
object_list_item_2: "2"
|
||||
# list comment
|
||||
list:
|
||||
- "1"
|
||||
`),
|
||||
).toEqual({
|
||||
list: ['1'],
|
||||
object: {
|
||||
object_title: 'object_title',
|
||||
object_list: [{ object_list_item_1: '1', object_list_item_2: '2' }],
|
||||
},
|
||||
date: new Date('2020-04-02T13:27:48.617Z'),
|
||||
image: '/media/netlify.png',
|
||||
title: 'title',
|
||||
template: 'post',
|
||||
});
|
||||
});
|
||||
test('does not fail on closing separator', () => {
|
||||
expect(yaml.fromFile('---\n[]\n---')).toEqual([]);
|
||||
});
|
||||
|
||||
test('parses single quoted string as string', () => {
|
||||
expect(yaml.fromFile('name: y')).toEqual({ name: 'y' });
|
||||
});
|
||||
|
||||
test('parses ISO date string as date', () => {
|
||||
expect(yaml.fromFile('date: 2020-04-02T16:08:03.327Z')).toEqual({
|
||||
date: new Date('2020-04-02T16:08:03.327Z'),
|
||||
});
|
||||
});
|
||||
|
||||
test('parses partial date string as string', () => {
|
||||
expect(yaml.fromFile('date: 2020-06-12')).toEqual({
|
||||
date: '2020-06-12',
|
||||
});
|
||||
expect(yaml.fromFile('date: 12-06-2012')).toEqual({
|
||||
date: '12-06-2012',
|
||||
});
|
||||
});
|
||||
|
||||
test('parses partial time value as string', () => {
|
||||
expect(yaml.fromFile('time: 10:05')).toEqual({
|
||||
time: '10:05',
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('toFile', () => {
|
||||
test('outputs valid yaml', () => {
|
||||
expect(yaml.toFile([])).toEqual('[]\n');
|
||||
});
|
||||
|
||||
test('should sort keys', () => {
|
||||
expect(yaml.toFile({ a: 'a', b: 'b', c: 'c', d: 'd' })).toEqual('a: a\nb: b\nc: c\nd: d\n');
|
||||
expect(yaml.toFile({ a: 'a', b: 'b', c: 'c', d: 'd' }, ['d', 'b', 'a', 'c'])).toEqual(
|
||||
'd: d\nb: b\na: a\nc: c\n',
|
||||
);
|
||||
expect(yaml.toFile({ a: 'a', b: 'b', c: 'c', d: 'd' }, ['d', 'b', 'c'])).toEqual(
|
||||
'a: a\nd: d\nb: b\nc: c\n',
|
||||
);
|
||||
});
|
||||
|
||||
test('should add comments', () => {
|
||||
expect(
|
||||
yaml.toFile({ a: 'a', b: { c: { d: 'd' } } }, [], { a: 'Comment a', 'b.c': 'Comment c' }),
|
||||
).toEqual('# Comment a\na: a\nb:\n # Comment c\n c:\n d: d\n');
|
||||
|
||||
const expected = `# template comment
|
||||
template: post
|
||||
# title comment
|
||||
title: title
|
||||
# image comment
|
||||
image: /media/netlify.png
|
||||
# date comment
|
||||
date: 2020-04-02T13:27:48.617Z
|
||||
# object comment
|
||||
object:
|
||||
# object_title comment
|
||||
object_title: object_title
|
||||
# object_list comment
|
||||
object_list:
|
||||
- object_list_item_1: "1"
|
||||
object_list_item_2: "2"
|
||||
# list comment
|
||||
list:
|
||||
- "1"
|
||||
`;
|
||||
|
||||
const result = yaml.toFile(
|
||||
{
|
||||
list: ['1'],
|
||||
object: {
|
||||
object_title: 'object_title',
|
||||
object_list: [{ object_list_item_1: '1', object_list_item_2: '2' }],
|
||||
},
|
||||
date: new Date('2020-04-02T13:27:48.617Z'),
|
||||
image: '/media/netlify.png',
|
||||
title: 'title',
|
||||
template: 'post',
|
||||
},
|
||||
['template', 'title', 'image', 'date', 'object', 'list'],
|
||||
{
|
||||
list: 'list comment',
|
||||
object: 'object comment',
|
||||
'object.object_title': 'object_title comment',
|
||||
'object.object_list': 'object_list comment',
|
||||
date: 'date comment',
|
||||
image: 'image comment',
|
||||
title: 'title comment',
|
||||
template: 'template comment',
|
||||
},
|
||||
);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
|
||||
expect(yaml.toFile({ a: 'a' }, [], { a: 'line 1\\nline 2' })).toEqual(
|
||||
'# line 1\n# line 2\na: a\n',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
97
node_modules/decap-cms-core/src/formats/formats.ts
generated
vendored
Normal file
97
node_modules/decap-cms-core/src/formats/formats.ts
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
import { List } from 'immutable';
|
||||
import { get } from 'lodash';
|
||||
|
||||
import yamlFormatter from './yaml';
|
||||
import tomlFormatter from './toml';
|
||||
import jsonFormatter from './json';
|
||||
import { FrontmatterInfer, frontmatterJSON, frontmatterTOML, frontmatterYAML } from './frontmatter';
|
||||
import { getCustomFormatsExtensions, getCustomFormatsFormatters } from '../lib/registry';
|
||||
|
||||
import type { Delimiter } from './frontmatter';
|
||||
import type { Collection, EntryObject, Format } from '../types/redux';
|
||||
import type { EntryValue } from '../valueObjects/Entry';
|
||||
import type { Formatter } from 'decap-cms-core';
|
||||
|
||||
export const frontmatterFormats = ['yaml-frontmatter', 'toml-frontmatter', 'json-frontmatter'];
|
||||
|
||||
export const formatExtensions = {
|
||||
yml: 'yml',
|
||||
yaml: 'yml',
|
||||
toml: 'toml',
|
||||
json: 'json',
|
||||
frontmatter: 'md',
|
||||
'json-frontmatter': 'md',
|
||||
'toml-frontmatter': 'md',
|
||||
'yaml-frontmatter': 'md',
|
||||
};
|
||||
|
||||
export function getFormatExtensions() {
|
||||
return { ...formatExtensions, ...getCustomFormatsExtensions() };
|
||||
}
|
||||
|
||||
export const extensionFormatters = {
|
||||
yml: yamlFormatter,
|
||||
yaml: yamlFormatter,
|
||||
toml: tomlFormatter,
|
||||
json: jsonFormatter,
|
||||
md: FrontmatterInfer,
|
||||
markdown: FrontmatterInfer,
|
||||
html: FrontmatterInfer,
|
||||
};
|
||||
|
||||
function formatByName(name: Format, customDelimiter?: Delimiter): Formatter {
|
||||
const formatters: Record<string, Formatter> = {
|
||||
yml: yamlFormatter,
|
||||
yaml: yamlFormatter,
|
||||
toml: tomlFormatter,
|
||||
json: jsonFormatter,
|
||||
frontmatter: FrontmatterInfer,
|
||||
'json-frontmatter': frontmatterJSON(customDelimiter),
|
||||
'toml-frontmatter': frontmatterTOML(customDelimiter),
|
||||
'yaml-frontmatter': frontmatterYAML(customDelimiter),
|
||||
...getCustomFormatsFormatters(),
|
||||
};
|
||||
if (name in formatters) {
|
||||
return formatters[name];
|
||||
}
|
||||
throw new Error(`No formatter available with name: ${name}`);
|
||||
}
|
||||
|
||||
function frontmatterDelimiterIsList(
|
||||
frontmatterDelimiter?: Delimiter | List<string>,
|
||||
): frontmatterDelimiter is List<string> {
|
||||
return List.isList(frontmatterDelimiter);
|
||||
}
|
||||
|
||||
export function resolveFormat(collection: Collection, entry: EntryObject | EntryValue) {
|
||||
// Check for custom delimiter
|
||||
const frontmatter_delimiter = collection.get('frontmatter_delimiter');
|
||||
const customDelimiter = frontmatterDelimiterIsList(frontmatter_delimiter)
|
||||
? (frontmatter_delimiter.toArray() as [string, string])
|
||||
: frontmatter_delimiter;
|
||||
|
||||
// If the format is specified in the collection, use that format.
|
||||
const formatSpecification = collection.get('format');
|
||||
if (formatSpecification) {
|
||||
return formatByName(formatSpecification, customDelimiter);
|
||||
}
|
||||
|
||||
// If a file already exists, infer the format from its file extension.
|
||||
const filePath = entry && entry.path;
|
||||
if (filePath) {
|
||||
const fileExtension = filePath.split('.').pop();
|
||||
if (fileExtension) {
|
||||
return get(extensionFormatters, fileExtension);
|
||||
}
|
||||
}
|
||||
|
||||
// If creating a new file, and an `extension` is specified in the
|
||||
// collection config, infer the format from that extension.
|
||||
const extension = collection.get('extension');
|
||||
if (extension) {
|
||||
return get(extensionFormatters, extension);
|
||||
}
|
||||
|
||||
// If no format is specified and it cannot be inferred, return the default.
|
||||
return formatByName('frontmatter', customDelimiter);
|
||||
}
|
||||
150
node_modules/decap-cms-core/src/formats/frontmatter.ts
generated
vendored
Normal file
150
node_modules/decap-cms-core/src/formats/frontmatter.ts
generated
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
import matter from 'gray-matter';
|
||||
|
||||
import tomlFormatter from './toml';
|
||||
import yamlFormatter from './yaml';
|
||||
import jsonFormatter from './json';
|
||||
|
||||
const Languages = {
|
||||
YAML: 'yaml',
|
||||
TOML: 'toml',
|
||||
JSON: 'json',
|
||||
} as const;
|
||||
|
||||
type Language = (typeof Languages)[keyof typeof Languages];
|
||||
|
||||
export type Delimiter = string | [string, string];
|
||||
type Format = { language: Language; delimiters: Delimiter };
|
||||
|
||||
const parsers = {
|
||||
toml: {
|
||||
parse: (input: string) => tomlFormatter.fromFile(input),
|
||||
stringify: (metadata: object, opts?: { sortedKeys?: string[] }) => {
|
||||
const { sortedKeys } = opts || {};
|
||||
return tomlFormatter.toFile(metadata, sortedKeys);
|
||||
},
|
||||
},
|
||||
json: {
|
||||
parse: (input: string) => {
|
||||
let JSONinput = input.trim();
|
||||
// Fix JSON if leading and trailing brackets were trimmed.
|
||||
if (JSONinput.slice(0, 1) !== '{') {
|
||||
JSONinput = '{' + JSONinput + '}';
|
||||
}
|
||||
return jsonFormatter.fromFile(JSONinput);
|
||||
},
|
||||
stringify: (metadata: object) => {
|
||||
let JSONoutput = jsonFormatter.toFile(metadata).trim();
|
||||
// Trim leading and trailing brackets.
|
||||
if (JSONoutput.slice(0, 1) === '{' && JSONoutput.slice(-1) === '}') {
|
||||
JSONoutput = JSONoutput.slice(1, -1);
|
||||
}
|
||||
return JSONoutput;
|
||||
},
|
||||
},
|
||||
yaml: {
|
||||
parse: (input: string) => yamlFormatter.fromFile(input),
|
||||
stringify: (
|
||||
metadata: object,
|
||||
opts?: { sortedKeys?: string[]; comments?: Record<string, string> },
|
||||
) => {
|
||||
const { sortedKeys, comments } = opts || {};
|
||||
return yamlFormatter.toFile(metadata, sortedKeys, comments);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
function inferFrontmatterFormat(str: string) {
|
||||
const lineEnd = str.indexOf('\n');
|
||||
const firstLine = str.slice(0, lineEnd !== -1 ? lineEnd : 0).trim();
|
||||
if (firstLine.length > 3 && firstLine.slice(0, 3) === '---') {
|
||||
// No need to infer, `gray-matter` will handle things like `---toml` for us.
|
||||
return;
|
||||
}
|
||||
switch (firstLine) {
|
||||
case '---':
|
||||
return getFormatOpts(Languages.YAML);
|
||||
case '+++':
|
||||
return getFormatOpts(Languages.TOML);
|
||||
case '{':
|
||||
return getFormatOpts(Languages.JSON);
|
||||
default:
|
||||
console.warn('Unrecognized front-matter format.');
|
||||
}
|
||||
}
|
||||
|
||||
export function getFormatOpts(format?: Language, customDelimiter?: Delimiter) {
|
||||
if (!format) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const formats: { [key in Language]: Format } = {
|
||||
yaml: { language: Languages.YAML, delimiters: '---' },
|
||||
toml: { language: Languages.TOML, delimiters: '+++' },
|
||||
json: { language: Languages.JSON, delimiters: ['{', '}'] },
|
||||
};
|
||||
|
||||
const { language, delimiters } = formats[format];
|
||||
|
||||
return {
|
||||
language,
|
||||
delimiters: customDelimiter || delimiters,
|
||||
};
|
||||
}
|
||||
|
||||
export class FrontmatterFormatter {
|
||||
format?: Format;
|
||||
|
||||
constructor(format?: Language, customDelimiter?: Delimiter) {
|
||||
this.format = getFormatOpts(format, customDelimiter);
|
||||
}
|
||||
|
||||
fromFile(content: string) {
|
||||
const format = this.format || inferFrontmatterFormat(content);
|
||||
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 }),
|
||||
};
|
||||
}
|
||||
|
||||
toFile(
|
||||
data: { body?: string } & Record<string, unknown>,
|
||||
sortedKeys?: string[],
|
||||
comments?: Record<string, string>,
|
||||
) {
|
||||
const { body = '', ...meta } = data;
|
||||
|
||||
// Stringify to YAML if the format was not set
|
||||
const format = this.format || getFormatOpts(Languages.YAML);
|
||||
|
||||
// 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';
|
||||
const file = matter.stringify(body, meta, {
|
||||
engines: parsers,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore `sortedKeys` is not recognized by gray-matter, so it gets passed through to the parser
|
||||
sortedKeys,
|
||||
comments,
|
||||
...format,
|
||||
});
|
||||
return trimLastLineBreak && file.slice(-1) === '\n' ? file.slice(0, -1) : file;
|
||||
}
|
||||
}
|
||||
|
||||
export const FrontmatterInfer = new FrontmatterFormatter();
|
||||
|
||||
export function frontmatterYAML(customDelimiter?: Delimiter) {
|
||||
return new FrontmatterFormatter(Languages.YAML, customDelimiter);
|
||||
}
|
||||
|
||||
export function frontmatterTOML(customDelimiter?: Delimiter) {
|
||||
return new FrontmatterFormatter(Languages.TOML, customDelimiter);
|
||||
}
|
||||
|
||||
export function frontmatterJSON(customDelimiter?: Delimiter) {
|
||||
return new FrontmatterFormatter(Languages.JSON, customDelimiter);
|
||||
}
|
||||
14
node_modules/decap-cms-core/src/formats/helpers.ts
generated
vendored
Normal file
14
node_modules/decap-cms-core/src/formats/helpers.ts
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
export function sortKeys<Item>(
|
||||
sortedKeys: string[],
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
selector: (a: Item) => string = (a: any) => a,
|
||||
) {
|
||||
return (a: Item, b: Item) => {
|
||||
const idxA = sortedKeys.indexOf(selector(a));
|
||||
const idxB = sortedKeys.indexOf(selector(b));
|
||||
if (idxA === -1 || idxB === -1) return 0;
|
||||
if (idxA > idxB) return 1;
|
||||
if (idxA < idxB) return -1;
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
9
node_modules/decap-cms-core/src/formats/json.ts
generated
vendored
Normal file
9
node_modules/decap-cms-core/src/formats/json.ts
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
export default {
|
||||
fromFile(content: string) {
|
||||
return JSON.parse(content);
|
||||
},
|
||||
|
||||
toFile(data: object) {
|
||||
return JSON.stringify(data, null, 2);
|
||||
},
|
||||
};
|
||||
33
node_modules/decap-cms-core/src/formats/toml.ts
generated
vendored
Normal file
33
node_modules/decap-cms-core/src/formats/toml.ts
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
import toml from '@iarna/toml';
|
||||
import tomlify from 'tomlify-j0.4';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import AssetProxy from '../valueObjects/AssetProxy';
|
||||
import { sortKeys } from './helpers';
|
||||
|
||||
function outputReplacer(_key: string, value: unknown) {
|
||||
if (dayjs.isDayjs(value)) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
return value.format(value._f);
|
||||
}
|
||||
if (value instanceof AssetProxy) {
|
||||
return `${value.path}`;
|
||||
}
|
||||
if (typeof value === 'number' && Number.isInteger(value)) {
|
||||
// Return the string representation of integers so tomlify won't render with tenths (".0")
|
||||
return value.toString();
|
||||
}
|
||||
// Return `false` to use default (`undefined` would delete key).
|
||||
return false;
|
||||
}
|
||||
|
||||
export default {
|
||||
fromFile(content: string) {
|
||||
return toml.parse(content);
|
||||
},
|
||||
|
||||
toFile(data: object, sortedKeys: string[] = []) {
|
||||
return tomlify.toToml(data, { replace: outputReplacer, sort: sortKeys(sortedKeys) });
|
||||
},
|
||||
};
|
||||
58
node_modules/decap-cms-core/src/formats/yaml.ts
generated
vendored
Normal file
58
node_modules/decap-cms-core/src/formats/yaml.ts
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
import yaml from 'yaml';
|
||||
|
||||
import { sortKeys } from './helpers';
|
||||
|
||||
import type { YAMLMap, YAMLSeq, Pair, Node } from 'yaml/types';
|
||||
|
||||
function addComments(items: Array<Pair>, comments: Record<string, string>, prefix = '') {
|
||||
items.forEach(item => {
|
||||
if (item.key !== undefined) {
|
||||
const itemKey = item.key.toString();
|
||||
const key = prefix ? `${prefix}.${itemKey}` : itemKey;
|
||||
if (comments[key]) {
|
||||
const value = comments[key].split('\\n').join('\n ');
|
||||
item.commentBefore = ` ${value}`;
|
||||
}
|
||||
if (Array.isArray(item.value?.items)) {
|
||||
addComments(item.value.items, comments, key);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const timestampTag = {
|
||||
identify: (value: unknown) => value instanceof Date,
|
||||
default: true,
|
||||
tag: '!timestamp',
|
||||
test: RegExp(
|
||||
'^' +
|
||||
'([0-9]{4})-([0-9]{2})-([0-9]{2})' + // YYYY-MM-DD
|
||||
'T' + // T
|
||||
'([0-9]{2}):([0-9]{2}):([0-9]{2}(\\.[0-9]+)?)' + // HH:MM:SS(.ss)?
|
||||
'Z' + // Z
|
||||
'$',
|
||||
),
|
||||
resolve: (str: string) => new Date(str),
|
||||
stringify: (value: Node) => (value as Date).toISOString(),
|
||||
} as const;
|
||||
|
||||
export default {
|
||||
fromFile(content: string) {
|
||||
if (content && content.trim().endsWith('---')) {
|
||||
content = content.trim().slice(0, -3);
|
||||
}
|
||||
return yaml.parse(content, { customTags: [timestampTag] });
|
||||
},
|
||||
|
||||
toFile(data: object, sortedKeys: string[] = [], comments: Record<string, string> = {}) {
|
||||
const contents = yaml.createNode(data) as YAMLMap | YAMLSeq;
|
||||
|
||||
addComments(contents.items, comments);
|
||||
|
||||
contents.items.sort(sortKeys(sortedKeys, item => item.key?.toString()));
|
||||
const doc = new yaml.Document();
|
||||
doc.contents = contents;
|
||||
|
||||
return doc.toString();
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user