11# requires -Version 5
22<#
33. SYNOPSIS
4- Regenerate breadcrumb + prev/next navigation blocks in every doc page.
4+ Regenerate breadcrumb + prev/next navigation blocks in every doc page,
5+ and emit Just-the-Docs YAML frontmatter for the gh-pages-built sidebar.
56
67. DESCRIPTION
78 Parses SUMMARY.md at the repo root, walks its tree depth-first to build a
8- linear reading order, and rewrites a marker-fenced nav block at the top
9- and bottom of every .md file referenced.
9+ linear reading order, and rewrites three sections of every .md file
10+ referenced:
1011
11- The top block is breadcrumbs (Home > Section > Page) followed by a
12- prev / next bar. The bottom block is a horizontal rule and a prev / next
13- bar. Both are wrapped in HTML comment markers (`<!-- nav-top -->` /
14- `<!-- nav-bottom -->`) so the script can rerun and rewrite cleanly when
15- SUMMARY.md changes.
12+ 1. YAML frontmatter at the very top of the file driving the
13+ Just-the-Docs sidebar nav (`title`, `parent`, `grand_parent`,
14+ `nav_order`, `has_children`).
15+ 2. A marker-fenced nav block (`<!-- nav-top -->`) below the frontmatter
16+ with breadcrumbs (Home > Section > Page) and a prev/next bar.
17+ 3. A marker-fenced bottom nav block (`<!-- nav-bottom -->`) with a
18+ horizontal rule and another prev/next bar.
19+
20+ All three are idempotent: rerunning the script after SUMMARY.md changes
21+ cleanly strips and rewrites without accumulating cruft.
1622
1723 Also strips the legacy "(this page is part of...)" preamble lines from
1824 Tutorial pages, since the new breadcrumb supersedes them.
1925
2026. NOTES
2127 Re-run after editing SUMMARY.md (adding pages, reordering, renaming).
28+ The frontmatter is consumed by Jekyll + just-the-docs; the marker-fenced
29+ nav blocks serve readers viewing the markdown raw on github.com.
2230#>
2331[CmdletBinding ()]
2432param ()
@@ -80,6 +88,38 @@ for ($i = 0; $i -lt $entries.Count; $i++) {
8088 $entries [$i ] | Add-Member - NotePropertyName Next - NotePropertyValue $next
8189}
8290
91+ # ---- Compute SiblingOrder + HasChildren (for Just-the-Docs frontmatter) -
92+
93+ # SiblingOrder: 1-based position among entries sharing the same immediate
94+ # parent. Top-level entries (depth 0) are siblings of each other under a
95+ # synthetic '<root>' key.
96+ $siblingCounter = @ {}
97+ foreach ($e in $entries ) {
98+ $parentKey =
99+ if ($e.Parents.Count -gt 0 ) {
100+ $e.Parents [$e.Parents.Count - 1 ].RelPath
101+ } else { ' <root>' }
102+ if (-not $siblingCounter.ContainsKey ($parentKey )) {
103+ $siblingCounter [$parentKey ] = 0
104+ }
105+ $siblingCounter [$parentKey ]++
106+ $e | Add-Member - NotePropertyName SiblingOrder - NotePropertyValue $siblingCounter [$parentKey ]
107+ }
108+
109+ # HasChildren: true if any other entry's immediate parent is this entry.
110+ # Just-the-Docs uses this to expand the page as a parent node in the sidebar.
111+ foreach ($e in $entries ) {
112+ $hasChildren = $false
113+ foreach ($other in $entries ) {
114+ if ($other.Parents.Count -gt 0 -and
115+ $other.Parents [$other.Parents.Count - 1 ].RelPath -eq $e.RelPath ) {
116+ $hasChildren = $true
117+ break
118+ }
119+ }
120+ $e | Add-Member - NotePropertyName HasChildren - NotePropertyValue $hasChildren
121+ }
122+
83123# ---- Helpers ----
84124
85125function Get-Rel ($fromFile , $toFile ) {
@@ -116,6 +156,34 @@ function Build-Breadcrumb($entry) {
116156 return ($parts -join ' > ' )
117157}
118158
159+ function Build-Frontmatter ($entry , $isRoot ) {
160+ # Just-the-Docs YAML frontmatter. Drives the sidebar nav structure.
161+ # On rerun, the strip regex below removes any existing frontmatter
162+ # block at the top of the file and this writes a fresh one.
163+ if ($isRoot ) {
164+ # The root README is the site landing page. Just-the-Docs treats
165+ # it as the home; nav_order: 0 keeps it first if it ever ends up
166+ # rendered in the sidebar.
167+ return " ---`n title: $ ( $entry.Title ) `n nav_order: 0`n ---"
168+ }
169+ $lines = @ (" title: $ ( $entry.Title ) " )
170+ if ($entry.Parents.Count -ge 1 ) {
171+ $immediate = $entry.Parents [$entry.Parents.Count - 1 ]
172+ $lines += " parent: $ ( $immediate.Title ) "
173+ }
174+ if ($entry.Parents.Count -ge 2 ) {
175+ # Just-the-Docs requires grand_parent for any page nested three
176+ # levels deep so the sidebar knows where to slot it.
177+ $grand = $entry.Parents [0 ]
178+ $lines += " grand_parent: $ ( $grand.Title ) "
179+ }
180+ $lines += " nav_order: $ ( $entry.SiblingOrder ) "
181+ if ($entry.HasChildren ) {
182+ $lines += " has_children: true"
183+ }
184+ return " ---`n " + ($lines -join " `n " ) + " `n ---"
185+ }
186+
119187function Build-PrevNext ($entry ) {
120188 $prevPart =
121189 if ($entry.Prev ) {
@@ -157,6 +225,15 @@ $legacyPreamble = "^\(this page is part of \[[^\]]+\]\([^)]+\)\)\s*\r?\n"
157225# the bottom-regex started consuming them. Allows blank lines between adjacent
158226# orphans (which is exactly the pattern previous buggy runs produced).
159227$trailingOrphanSep = " (?:\r?\n\s*---[ \t]*)+\s*$"
228+ # Existing YAML frontmatter at the very top of the file. The keyed-content
229+ # requirement (`(?:[a-zA-Z_]\w*: [^\r\n]*\r?\n)+` between the delimiters)
230+ # keeps the regex from misfiring on a file that genuinely opens with a bare
231+ # `---` horizontal rule and no frontmatter, AND uses [^\r\n]* (not .*) so the
232+ # value portion can't span newlines — important because on the second run
233+ # the file does have frontmatter, and a greedy .* would otherwise extend
234+ # down to the next `---` separator and consume the entire body. Trailing
235+ # blank lines are also consumed so successive runs don't drift the body.
236+ $frontmatterRegex = " \A---\r?\n(?:[a-zA-Z_]\w*: [^\r\n]*\r?\n)+---\r?\n\r?\n?"
160237
161238$touched = 0
162239foreach ($e in $entries ) {
@@ -167,6 +244,10 @@ foreach ($e in $entries) {
167244 $path = $e.AbsPath.Path
168245 $body = [System.IO.File ]::ReadAllText($path )
169246
247+ # Strip existing frontmatter at the very top of the file FIRST so the
248+ # nav-top strip below sees the actual `<!-- nav-top -->` marker at the
249+ # head of the body.
250+ $body = [regex ]::Replace($body , $frontmatterRegex , ' ' )
170251 # Strip existing nav blocks (anywhere in the file, idempotent).
171252 $body = [regex ]::Replace($body , $navTopRegex , ' ' )
172253 $body = [regex ]::Replace($body , $navBottomRegex , ' ' )
@@ -199,7 +280,8 @@ foreach ($e in $entries) {
199280 # Trim leading blank lines that may have been left by the strip, and
200281 # trailing whitespace, so the output is tidy.
201282 $body = $body.TrimStart (" `r " , " `n " , " " , " `t " ).TrimEnd()
202- $newBody = $topBlock + $body + $bottomBlock + " `n "
283+ $frontmatter = Build-Frontmatter $e ($e -eq $rootEntry )
284+ $newBody = " $frontmatter `n`n $topBlock$body$bottomBlock `n "
203285
204286 [System.IO.File ]::WriteAllText($path , $newBody , [System.Text.UTF8Encoding ]::new($false ))
205287 $touched ++
0 commit comments