Skip to content

fix(compare): apply cleanDataKeys normalization before conflict comparison#483

Draft
hendrikheil wants to merge 1 commit into
nuxt-content:mainfrom
hendrikheil:fix/false-conflict-meta-fields-comparison
Draft

fix(compare): apply cleanDataKeys normalization before conflict comparison#483
hendrikheil wants to merge 1 commit into
nuxt-content:mainfrom
hendrikheil:fix/false-conflict-meta-fields-comparison

Conversation

@hendrikheil

Copy link
Copy Markdown
Contributor

Problem

When opening certain pages in Studio, a false conflict dialog appeared showing a diff between the "GitHub" and "Website" versions — even though the file hadn't actually changed remotely.

The root cause: @nuxt/content stores frontmatter fields that aren't defined in the collection schema inside document.meta rather than as top-level columns. For example, a page with navigation: hidden and a sitemap: block in its frontmatter would end up with those fields at document.meta.navigation and document.meta.sitemap rather than document.navigation / document.sitemap.

checkConflict calls isDocumentMatchingContent to check whether the stored document still matches the remote file. This function re-parses the raw remote markdown via documentFromContent, which puts all frontmatter at top level. It then called doObjectsMatch(generatedDocumentData, documentData), where generatedDocumentData had all keys at the top level but documentData (the stored document) had some of them tucked inside meta. doObjectsMatch iterates over the base (generated) side's keys and treats missing values on the target (stored) side as mismatches — so it returned false, triggering the conflict path, even though no real change had occurred.

Fix

The rendering path (contentFromMarkdownDocument) already calls cleanDataKeys on the stored document before producing markdown from it. cleanDataKeys expands meta to root level, strips reserved keys (id, stem, path, __hash__, etc.), removes top-level nulls and empty arrays, and normalises navigation/seo defaults.

I applied the same normalization to both sides inside isDocumentMatchingContent:

// Before
return doObjectsMatch(generatedDocumentData, documentData)

// After
return doObjectsMatch(
  cleanDataKeys(generatedDocument) as Record<string, unknown>,
  cleanDataKeys(document) as Record<string, unknown>,
)

This brings the conflict check in line with what the rest of the pipeline sees, so a stored document and a freshly-parsed document of the same content are structurally equivalent before comparison — regardless of whether @nuxt/content chose to store certain fields at top level or inside meta.

Note that areDocumentsEqual (used elsewhere) already handled this correctly by spreading { ...documentData, ...(meta || {}) } before comparing. isDocumentMatchingContent was the outlier.

Test

Added a regression test in document.test.ts that reproduces the exact shape of document that was triggering false conflicts: frontmatter contains navigation and sitemap keys that @nuxt/content stored inside meta rather than at the top level. The test was failing before this fix and passes after.

Checklist

  • Root cause identified and fixed (not a band-aid)
  • Regression test added that fails without the fix
  • No changes to draft.ts / checkConflict — the fix is in the right layer
  • Existing test suite passes (pnpm test document → 19/19)

🤖 Generated with Claude Code

…rison

When @nuxt/content stores frontmatter fields in `meta` rather than as
top-level columns (because they aren't defined in the collection schema),
`isDocumentMatchingContent` was returning false for unchanged files —
causing Studio to show a spurious conflict dialog on every open.

The freshly-parsed document (from `documentFromContent`) has all
frontmatter at top level, while the stored document may have some fields
inside `meta`. `doObjectsMatch` iterated the generated doc's keys and
found mismatches against `undefined` on the stored side.

The rendering path (`contentFromMarkdownDocument`) already calls
`cleanDataKeys` which expands `meta` to root and strips nulls/reserved
keys. Apply the same normalization to both sides inside
`isDocumentMatchingContent` so the comparison is consistent with what
the rest of the pipeline sees.

Adds a regression test covering documents where `navigation` and
`sitemap` are stored in `meta` by @nuxt/content.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

@hendrikheil is attempting to deploy a commit to the Nuxt Team on Vercel.

A member of the Team first needs to authorize it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant