Skip to content

Plugin Docs: Add frontmatter validation rules#2500

Open
sunker wants to merge 11 commits intomainfrom
plugin-docs/fm-validation-rules
Open

Plugin Docs: Add frontmatter validation rules#2500
sunker wants to merge 11 commits intomainfrom
plugin-docs/fm-validation-rules

Conversation

@sunker
Copy link
Contributor

@sunker sunker commented Mar 2, 2026

What this PR does / why we need it:

This PR adds frontmatter validation to the docs CLI and a rehype-strip-h1 plugin to the docs parser. The validator checks that every markdown page has a frontmatter block with title and description as strings, catches wrong types, flags invalid or duplicate custom slugs and detects duplicate sidebar_position values among sibling pages. The h1-stripping plugin ensures rendered pages don't show a double title — since the page title comes from frontmatter any # Heading in the body is silently dropped by the parser. sidebar_position is optional - pages without it sort last.

Which issue(s) this PR fixes:

Part of https://github.com/grafana/grafana-catalog-team/issues/769

Special notes for your reviewer:

  • Duplicate position/slug checks use a two-pass approach (count then report) so every offending file is flagged, not just the second one
  • no-redeclare ESLint override is scoped to plugin-docs-cli only, to allow the const Foo = {} as const; type Foo = ... pattern in types.ts
  • All diagnostics include file path and line number for editor integration

@github-actions
Copy link
Contributor

github-actions bot commented Mar 2, 2026

Hello! 👋 This repository uses Auto for releasing packages using PR labels.

✨ This PR can be merged. It will not be considered when calculating future versions of the npm packages and will not appear in the changelogs.

@sunker sunker added release Create a release when this pr is merged no-changelog Don't include in changelog and version calculations and removed release Create a release when this pr is merged labels Mar 2, 2026
'no-redeclare': 'off',
// ts compiler handles redeclaration; using the same name for a const object and its derived type (e.g. `const Foo = {...} as const; type Foo = ...`) is valid ts but trips eslint
},
},
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See

export const Rule = {
// filesystem rules
HasMarkdown: 'has-markdown-files',
RootIndex: 'root-index-exists',
NestedDirIndex: 'nested-dir-has-index',
NoSpaces: 'no-spaces-in-names',
ValidNaming: 'valid-file-naming',
NoEmptyDir: 'no-empty-directories',
NoSymlinks: 'no-symlinks',
AllowedFileTypes: 'allowed-file-types',
// frontmatter rules
BlockExists: 'frontmatter-block-exists',
ValidYaml: 'frontmatter-valid-yaml',
RequiredFields: 'frontmatter-required-fields',
FieldTypes: 'frontmatter-field-types',
ValidSlug: 'frontmatter-valid-slug',
NoH1: 'no-h1-heading',
DuplicatePosition: 'no-duplicate-sidebar-position',
DuplicateSlug: 'no-duplicate-slugs',
} as const;
export type Rule = (typeof Rule)[keyof typeof Rule];

@sunker sunker marked this pull request as ready for review March 2, 2026 08:53
@sunker sunker requested a review from a team as a code owner March 2, 2026 08:53
Copilot AI review requested due to automatic review settings March 2, 2026 08:53
@sunker sunker requested a review from a team as a code owner March 2, 2026 08:53
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enhances the plugin docs toolchain by (1) enforcing frontmatter requirements via the docs CLI validator and (2) ensuring rendered pages don’t show a “double title” by stripping h1 headings from parsed markdown content.

Changes:

  • Add a rehype-strip-h1 plugin to remove h1 elements from parsed markdown and update parser/server tests accordingly.
  • Introduce frontmatter validation rules in plugin-docs-cli (required fields, type checks, slug checks, duplicate slug, duplicate sibling sidebar_position, and h1 warnings).
  • Standardize validator rule identifiers via a typed Rule constant and update existing filesystem validation/tests to use it.

Reviewed changes

Copilot reviewed 17 out of 18 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/plugin-docs-parser/src/types.ts Make sidebar_position optional in the shared frontmatter type.
packages/plugin-docs-parser/src/plugins/rehype-strip-h1.ts New rehype plugin that removes h1 nodes from the HAST tree.
packages/plugin-docs-parser/src/plugins/rehype-strip-h1.test.ts Unit tests for the rehype-strip-h1 plugin behavior.
packages/plugin-docs-parser/src/parser.ts Insert rehypeStripH1 into the markdown parsing pipeline.
packages/plugin-docs-parser/src/parser.test.ts Update parser tests to reflect h1 stripping and add a targeted test.
packages/plugin-docs-cli/src/validation/types.ts Add typed Rule identifiers and type diagnostics to use them.
packages/plugin-docs-cli/src/validation/rules/index.ts Register the new frontmatter rule runner alongside filesystem rules.
packages/plugin-docs-cli/src/validation/rules/frontmatter.ts New frontmatter validation rule implementation (field checks + duplicate checks).
packages/plugin-docs-cli/src/validation/rules/frontmatter.test.ts Add test coverage for frontmatter rule diagnostics and duplicate detection.
packages/plugin-docs-cli/src/validation/rules/filesystem.ts Switch filesystem diagnostics to use typed Rule values.
packages/plugin-docs-cli/src/validation/rules/filesystem.test.ts Update filesystem tests to use Rule and add symlink / allowed-file-type tests.
packages/plugin-docs-cli/src/validation/format.test.ts Update formatting tests to use typed Rule values.
packages/plugin-docs-cli/src/validation/engine.test.ts Update engine tests to use typed Rule values.
packages/plugin-docs-cli/src/server/views/docs-layout.ejs Render the page title in the layout and avoid duplicative h1 usage in the header.
packages/plugin-docs-cli/src/server/server.test.ts Update server HTML assertions to match the new layout/page title behavior.
packages/plugin-docs-cli/src/scanner.ts Stop requiring sidebar_position as a mandatory frontmatter field during scanning.
eslint.config.js Disable no-redeclare for plugin-docs-cli TS/TSX files to support const Foo + type Foo patterns.
package-lock.json Lockfile update reflecting dependency metadata changes.

@grafana-plugins-platform-bot grafana-plugins-platform-bot bot moved this from 📬 Triage to 🔬 In review in Grafana Catalog Team Mar 2, 2026
const findings = await checkFrontmatter(input(invalidDocs));

const typeErrors = findings.filter((f) => f.rule === Rule.FieldTypes);
expect(typeErrors.length).toBeGreaterThanOrEqual(3);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use toBeEqual(3) because if something changes in the code that causes side effects this test will not catch it due to the way you are doing validations of contents in the next lines.

// title on line 2, description on line 3
await writeFile(join(tmp, 'page.md'), '---\ntitle: 123\ndescription: Valid\nsidebar_position: 1\n---\n');

const findings = await checkFrontmatter(input(tmp));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am writing it only in this test but it applies for al the other tests: check how many findings there are and make it strict to that number of findings.

The problem is you are using .find in an array to check if there's an specific issue but that means if findings has more values (which is unexpected for this test) it won't fail

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

Labels

no-changelog Don't include in changelog and version calculations

Projects

Status: 🔬 In review

Development

Successfully merging this pull request may close these issues.

3 participants