Skip to content

feat(core): navigation graph — frontmatter parser, meta.json reader, prev/next, breadcrumb - #2

Merged
Shewart merged 2 commits into
mainfrom
feat/core-navigation-graph
Jul 7, 2026
Merged

feat(core): navigation graph — frontmatter parser, meta.json reader, prev/next, breadcrumb#2
Shewart merged 2 commits into
mainfrom
feat/core-navigation-graph

Conversation

@Shewart

@Shewart Shewart commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Ships the foundation everything downstream builds on: a walkable navigation tree over your content/ folder, with typed frontmatter reads, polymorphic meta.json support, and the queries every ShellDocs primitive will need (ResolveByUrl, GetPrevNext, GetBreadcrumb, Flatten).

What ships to ShellDocs.Core

File Public API
NavigationNode.cs NodeKind enum (Page / Section / Divider). Simple POCO — Url, Title, Description, Category, Order, Path, Kind, Parent, Children
Heading.cs record Heading(int Level, string Text, string Id) — pulled to its own file for reuse by ShellDocs.Markdown in the next branch
FrontmatterParser.cs Parse(string) → ParsedDocument(Frontmatter, Body). Robust against missing / unclosed / malformed YAML. GetValue<T>() extension for typed reads
MetaJson.cs Parse(json) → MetaJson?. Four entry kinds (PageRef / Divider / Subsection) via a custom Utf8JsonReader converter that dispatches on JSON token type
NavigationGraph.cs ResolveByUrl, GetPrevNext, GetBreadcrumb, Flatten. URL-indexed at construction — O(1) lookup
NavigationGraphBuilder.cs Build(contentRoot) — walks folders, reads meta.json if present else alphabetical, resolves subsections recursively, silently skips typos (build-time warnings come in feat/cli-init)

Design decisions worth calling out

URL normalization is case-insensitive and trailing-slash-tolerant on lookup. graph.ResolveByUrl("/BUTTON"), "/button/", "button" all resolve the same node. Preserves the URL as authored for display; only the lookup key is normalized.

Custom Utf8JsonReader converter for meta.json. Each entry in the pages array is polymorphic — string slug, literal "---", or nested subsection object. System.Text.Json's built-in polymorphism assumes a discriminator; here we dispatch on JsonTokenType (string vs. object) instead.

meta.json typos are silently dropped, not thrown. A slug in meta.json that doesn't match any .md file or subfolder is skipped rather than crashing the build. Rationale: docs authors will hit this constantly during content-authoring flow; a hard crash there breaks the write→see loop. The build-time warning path lives in feat/cli-init where user-facing errors are the right pattern.

Folder-level meta.json ordering vs. alphabetical fallback. When present, meta.json fully controls the order and titles. When absent, subfolders (as Section nodes) are sorted alphabetically first, then .md pages by order frontmatter (default 0) with title as tiebreaker.

Root node is synthetic. Root has Kind = Section, empty title, Url = "/". Its children are the top-level content. Keeps recursion simple.

Path preserved on Section nodes. Points at the folder so feat/cli-dev-build's dev-mode file watcher can rescan just-changed folders without rebuilding the whole graph.

Test coverage

22 new tests, 24 total, all green.

Test class Tests Coverage
FrontmatterParserTests 6 Real frontmatter, missing/empty document, unclosed ---, invalid YAML, GetValue<T> type coercion
MetaJsonTests 4 Flat slug list, "---" divider, nested subsection object, blank input → null
NavigationGraphBuilderTests 12 Single file, frontmatter title vs. slug fallback, nested folders as sections, meta.json ordering, dividers, subsections, unknown-slug tolerance, missing content root throws, GetPrevNext adjacency, GetPrevNext boundaries, GetBreadcrumb walks up from leaf, case-insensitive + trailing-slash URL lookup
ScaffoldingTests 2 Retained for MarkdownPipelineFactory + StarterPageTemplate smoke

Tests use a per-test temp directory via IDisposable so parallel runs don't collide.

Verified green

  • dotnet build shelldocs.slnx → 0 warnings, 0 errors
  • dotnet test shelldocs.slnx → 24/24, ~630ms
  • Every public API on ShellDocs.Core is called from at least one test

What this unblocks

  • feat/markdown-pipeline — needs the NavigationNode shape locked in so the pipeline knows what it's populating
  • feat/components-shell — needs the navigation graph as its DocsSidebar data source
  • feat/search-primitives — the search indexer walks the graph
  • Every doc primitive that uses PrevNextNav / Breadcrumb / Sidebar now has a canonical source of truth

Files

  • 6 new .cs files in src/ShellDocs.Core/ (~430 LOC total, no XML-comment bloat)
  • 3 new test files + 1 updated in tests/ShellDocs.Tests/

Shewart added 2 commits July 7, 2026 21:17
…. Add NavigationGraph, NavigationNode, and related builders to manage document structure. Introduce FrontmatterParser for YAML frontmatter extraction and MetaJson for metadata handling.
…nd NavigationGraphBuilder. Validate parsing, handling of edge cases, and document structure resolution.
Copilot AI review requested due to automatic review settings July 7, 2026 19:19

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Shewart
Shewart merged commit 722e758 into main Jul 7, 2026
1 check passed
@Shewart
Shewart deleted the feat/core-navigation-graph branch July 17, 2026 15:50
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.

2 participants