Frontmatter Suite
Require valid frontmatter on every Markdown and MDX content file with registerFrontmatterSuite.
Summary
- Recursively scans the configured content directories for
.mdand.mdxfiles. - Checks that frontmatter is present, closed, and declares every required field.
- Separates blog posts from docs so each gets its own required-field set.
Why Use This Suite?
- Guarantees every page ships with the SEO fields a docs site needs to rank and render.
- Catches missing or placeholder frontmatter during the test run, before a broken page reaches the build.
- Applies the same required-field standard across every docs project, so metadata never varies from one repository to the next.
Examples
Usage
src/tests/frontmatter.test.ts
import { registerFrontmatterSuite } from '@cbnventures/nova/rules/vitest';
import * as vitest from 'vitest';
registerFrontmatterSuite({
vitest,
enable: 'all',
requiredFields: ['id', 'title', 'description', 'keywords', 'tags'],
});
The requiredFields list has no default, so you must declare which keys every non-blog page needs. Pass placeholderSentinel and placeholderBodyPrefix only if your authoring flow uses placeholder markers.
Configuration
| Field | Type | Default | Description |
|---|---|---|---|
requiredFields | string[] | required | Frontmatter keys every non-blog page must declare. |
requiredBlogFields | string[] | ['title', 'authors', 'tags'] | Frontmatter keys every blog post must declare. |
contentDirs | string[] | ['docs', 'blog'] | Directories, relative to rootDir, scanned recursively for content files. |
blogDir | string | 'blog' | Which content directory is the blog, selecting the blog required-field set. |
indexSlug | string | null | null | Expected id for index pages; null disables the index-to-slug remap. |
placeholderSentinel | string | disabled | Single-value marker that fails description, keywords, and tags checks. |
placeholderBodyPrefix | string | disabled | Body prefix that downgrades a file's issues from failures to warnings. |
rootDir | string | process.cwd() | Base directory the contentDirs resolve against. |
enable | 'all' | ToggleKey[] | required | Which checks run. Use 'all' for every check, or a list to freeze an exact set. |
Toggle Keys
| Toggle Key | Description |
|---|---|
frontmatter-present | Requires a file to open with a frontmatter block. |
frontmatter-closed | Requires the frontmatter block to have a closing delimiter. |
required-fields-present-docs | Requires every key in requiredFields on non-blog pages. |
required-fields-present-blog | Requires every key in requiredBlogFields on blog posts. |
id-matches-filename | Requires the id field to match the file name, honoring indexSlug. |
description-not-placeholder | Requires description to differ from the placeholder sentinel. |
keywords-not-placeholder | Requires keywords entries to differ from the placeholder sentinel. |
keywords-not-empty | Requires the keywords list to contain at least one entry. |
tags-not-placeholder | Requires tags entries to differ from the placeholder sentinel. |
tags-not-empty | Requires the tags list to contain at least one entry. |
placeholder-pages-warn-not-fail | Downgrades issues on placeholder pages to warnings when a prefix is set. |
Troubleshooting
- A missing content directory throws — The suite skips directories that do not exist. Confirm the path is correct if a configured directory is expected to be present.
- Placeholder checks never fire —
placeholderSentinelis disabled by default. Set it to your marker value to enable the placeholder checks. - Index pages fail
id-matches-filename— SetindexSlugto the expected id so index files map to your chosen slug.