From 83797a772a3af6e5753e9eccc4b48edd61e52c0a Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 8 Dec 2025 18:35:20 +0530 Subject: [PATCH 1/4] fix(frontmatter): improve duplicate frontmatter key error handling --- .../decap-cms-core/src/formats/frontmatter.ts | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/packages/decap-cms-core/src/formats/frontmatter.ts b/packages/decap-cms-core/src/formats/frontmatter.ts index 814ede8e3d6b..11b3f75a3a83 100644 --- a/packages/decap-cms-core/src/formats/frontmatter.ts +++ b/packages/decap-cms-core/src/formats/frontmatter.ts @@ -100,6 +100,31 @@ export class FrontmatterFormatter { fromFile(content: string) { const format = this.format || inferFrontmatterFormat(content); + + // Duplicate key detection for yaml frontmatter + { + const lines = content.split('\n'); + // Detect duplicate keys in frontmatter (YAML only) + if (!this.format || this.format.language === 'yaml') { + const keyCounts: Record = {}; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i].trim(); + + // Match YAML key: value pattern + const match = line.match(/^([A-Za-z0-9_\-]+):/); + if (!match) continue; + + const key = match[1]; + keyCounts[key] = (keyCounts[key] || 0) + 1; + + if (keyCounts[key] > 1) { + throw new Error(`Duplicate frontmatter key "${key}" found on line ${i + 1}.`); + } + } + } + } + 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. From db4c69b798db0e058d343c829f31e97265ce9dbb Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 8 Dec 2025 20:34:04 +0530 Subject: [PATCH 2/4] fix(frontmatter): improve duplicate key warning with file and line info --- packages/decap-cms-core/src/formats/frontmatter.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/decap-cms-core/src/formats/frontmatter.ts b/packages/decap-cms-core/src/formats/frontmatter.ts index 11b3f75a3a83..3c3fdbfa1388 100644 --- a/packages/decap-cms-core/src/formats/frontmatter.ts +++ b/packages/decap-cms-core/src/formats/frontmatter.ts @@ -98,7 +98,7 @@ export class FrontmatterFormatter { this.format = getFormatOpts(format, customDelimiter); } - fromFile(content: string) { + fromFile(content: string, filePath?: string) { const format = this.format || inferFrontmatterFormat(content); // Duplicate key detection for yaml frontmatter @@ -118,8 +118,10 @@ export class FrontmatterFormatter { const key = match[1]; keyCounts[key] = (keyCounts[key] || 0) + 1; + const source = filePath ?? 'unknown file'; + if (keyCounts[key] > 1) { - throw new Error(`Duplicate frontmatter key "${key}" found on line ${i + 1}.`); + console.warn(`Duplicate frontmatter key "${key}" in ${source} at line ${i + 1}`); } } } From a465a3bf2b2238273d05e30dca07e983e6ca3402 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 14 Dec 2025 20:15:50 +0530 Subject: [PATCH 3/4] fix(frontmatter): made duplicate detection path awarness using indendation --- .../decap-cms-core/src/formats/frontmatter.ts | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/packages/decap-cms-core/src/formats/frontmatter.ts b/packages/decap-cms-core/src/formats/frontmatter.ts index 3c3fdbfa1388..c43533341bf8 100644 --- a/packages/decap-cms-core/src/formats/frontmatter.ts +++ b/packages/decap-cms-core/src/formats/frontmatter.ts @@ -102,27 +102,43 @@ export class FrontmatterFormatter { const format = this.format || inferFrontmatterFormat(content); // Duplicate key detection for yaml frontmatter + { - const lines = content.split('\n'); - // Detect duplicate keys in frontmatter (YAML only) if (!this.format || this.format.language === 'yaml') { - const keyCounts: Record = {}; + const lines = content.split('\n'); + const seenPaths = new Set(); + const pathStack: { indent: number; key: string }[] = []; for (let i = 0; i < lines.length; i++) { - const line = lines[i].trim(); + const rawLine = lines[i]; + const line = rawLine.trim(); + + // Skip empty lines and comments + if (!line || line.startsWith('#')) continue; - // Match YAML key: value pattern - const match = line.match(/^([A-Za-z0-9_\-]+):/); + const match = rawLine.match(/^(\s*)([A-Za-z0-9_\-]+):/); if (!match) continue; - const key = match[1]; - keyCounts[key] = (keyCounts[key] || 0) + 1; + const indent = match[1].length; + const key = match[2]; + + // Pop stack until current indent level is valid + while (pathStack.length > 0 && pathStack[pathStack.length - 1].indent >= indent) { + pathStack.pop(); + } + + const fullPath = [...pathStack.map(p => p.key), key].join('.'); const source = filePath ?? 'unknown file'; - if (keyCounts[key] > 1) { - console.warn(`Duplicate frontmatter key "${key}" in ${source} at line ${i + 1}`); + if (seenPaths.has(fullPath)) { + console.warn(`Duplicate frontmatter key "${fullPath}" in ${source} at line ${i + 1}`); + } else { + seenPaths.add(fullPath); } + + // Push current key for nested children + pathStack.push({ indent, key }); } } } From 5327cc3b7a7ffc7b6fd48f7b0f2c2e609df3c8f8 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 15 Dec 2025 14:39:22 +0530 Subject: [PATCH 4/4] fix(frontmatter): removed backslash --- packages/decap-cms-core/src/formats/frontmatter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/decap-cms-core/src/formats/frontmatter.ts b/packages/decap-cms-core/src/formats/frontmatter.ts index c43533341bf8..d552bec7c1ff 100644 --- a/packages/decap-cms-core/src/formats/frontmatter.ts +++ b/packages/decap-cms-core/src/formats/frontmatter.ts @@ -116,7 +116,7 @@ export class FrontmatterFormatter { // Skip empty lines and comments if (!line || line.startsWith('#')) continue; - const match = rawLine.match(/^(\s*)([A-Za-z0-9_\-]+):/); + const match = rawLine.match(/^(\s*)([A-Za-z0-9_-]+):/); if (!match) continue; const indent = match[1].length;