Skip to content

Commit cbe162d

Browse files
codeafridimartinjagodicyanthomasdev
authored
fix(frontmatter): improve duplicate frontmatter key error handling (#7679)
* fix(frontmatter): improve duplicate frontmatter key error handling * fix(frontmatter): improve duplicate key warning with file and line info * fix(frontmatter): made duplicate detection path awarness using indendation * fix(frontmatter): removed backslash * feat: use `yaml` diagnostics --------- Co-authored-by: Martin Jagodic <jagodicmartin1@gmail.com> Co-authored-by: Yan <61414485+yanthomasdev@users.noreply.github.com>
1 parent 8a0d53f commit cbe162d

3 files changed

Lines changed: 64 additions & 1 deletion

File tree

packages/decap-cms-core/src/formats/__tests__/frontmatter.spec.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,27 @@ describe('Frontmatter', () => {
7272
});
7373
});
7474

75+
it('should throw on duplicate frontmatter keys', () => {
76+
expect(() =>
77+
FrontmatterInfer.fromFile('---\ntitle: Hello\ntitle: World\n---\nContent'),
78+
).toThrow(/Map keys must be unique/);
79+
});
80+
81+
it('should throw on duplicate frontmatter keys with explicit YAML format', () => {
82+
expect(() =>
83+
frontmatterYAML().fromFile('---\ntitle: Hello\ntitle: World\n---\nContent'),
84+
).toThrow(/Map keys must be unique/);
85+
});
86+
87+
it('should not throw when body contains YAML-like patterns', () => {
88+
expect(
89+
FrontmatterInfer.fromFile('---\ntitle: Hello\n---\ntitle: this is not a duplicate'),
90+
).toEqual({
91+
title: 'Hello',
92+
body: 'title: this is not a duplicate',
93+
});
94+
});
95+
7596
it('should stringify YAML with --- delimiters', () => {
7697
expect(
7798
FrontmatterInfer.toFile({

packages/decap-cms-core/src/formats/__tests__/yaml.spec.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,33 @@ describe('yaml', () => {
8585
time: '10:05',
8686
});
8787
});
88+
89+
test('throws on duplicate keys', () => {
90+
expect(() => yaml.fromFile('title: Hello\ntitle: World')).toThrow(
91+
/Map keys must be unique; "title" is repeated/,
92+
);
93+
});
94+
95+
test('throws on duplicate nested keys', () => {
96+
expect(() => yaml.fromFile('nested:\n a: 1\n a: 2')).toThrow(
97+
/Map keys must be unique; "a" is repeated/,
98+
);
99+
});
100+
101+
test('does not throw when same key appears in different nested objects', () => {
102+
expect(yaml.fromFile('obj1:\n name: foo\nobj2:\n name: bar')).toEqual({
103+
obj1: { name: 'foo' },
104+
obj2: { name: 'bar' },
105+
});
106+
});
107+
108+
test('logs warnings to console.warn', () => {
109+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
110+
// Valid YAML should produce no warnings
111+
yaml.fromFile('title: Hello');
112+
expect(warnSpy).not.toHaveBeenCalled();
113+
warnSpy.mockRestore();
114+
});
88115
});
89116
describe('toFile', () => {
90117
test('outputs valid yaml', () => {

packages/decap-cms-core/src/formats/yaml.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,22 @@ export default {
4141
if (content && content.trim().endsWith('---')) {
4242
content = content.trim().slice(0, -3);
4343
}
44-
return yaml.parse(content, { customTags: [timestampTag] });
44+
45+
const doc = yaml.parseDocument(content, {
46+
customTags: [timestampTag],
47+
prettyErrors: true,
48+
});
49+
50+
for (const warn of doc.warnings) {
51+
console.warn(`YAML warning: ${warn.message}`);
52+
}
53+
54+
if (doc.errors.length > 0) {
55+
const messages = doc.errors.map(e => e.message).join('\n');
56+
throw new Error(`YAML parsing error:\n${messages}`);
57+
}
58+
59+
return doc.toJSON();
4560
},
4661

4762
toFile(data: object, sortedKeys: string[] = [], comments: Record<string, string> = {}) {

0 commit comments

Comments
 (0)