Update-ReadmeIndex.ps1
<#PSScriptInfo
.VERSION 1.0.1 .AUTHOR Roman Kuzmin .COPYRIGHT (c) Roman Kuzmin .TAGS Markdown README Index TOC .GUID 0ab2bbcb-6552-415f-a102-7799e8a051b8 .LICENSEURI http://www.apache.org/licenses/LICENSE-2.0 .PROJECTURI https://github.com/nightroman/PowerShelf #> <# .Synopsis Updates README index from content directories. Author: Roman Kuzmin .Description The command scans the contents recursively, finds README.md files and builds the index from their first line headings in the root README.md. The generated list with links is inserted into the root README.md. The index start/end marks are defined by the HTML comments like: given $Content='docs', marks are '<!--docs-->'. .Parameter Content Specifies the directory to scan with the path relative to root. Use '/' as directory separators if it is not the top directory. .Parameter Root Specifies the root directory with README.md to be updated. The typical use case is a git repository root. Default: the current location .Parameter Depth Specifies the recursive scan depth. Default: 0, just top directories. .Parameter Descending Tells to sort top directories descending. .Parameter NoWarning Tells not to write warnings about no README. .Parameter Skip The script returning true for the directories to skip. Argument 1: directory path like $Content[/dir1[/...]] .Link Example: https://github.com/nightroman/PowerShellTraps .Link https://github.com/nightroman/PowerShelf #> [CmdletBinding()] param( [Parameter(Position=0, Mandatory=1)] [string]$Content , [string]$Root = '.' , [int]$Depth , [switch]$Descending , [switch]$NoWarning , [scriptblock]$Skip ) $ErrorActionPreference = 1 trap { Write-Error $_ } # Gets markdown list lines with links to folders recursively. function Get-List($Path, $Level) { $indent = ' ' * $Level # get and sort (top) directory names $dirNames = Get-ChildItem -LiteralPath $Path -Name -Directory if ($Level -eq 0) { $dirNames = $dirNames | Sort-Object -Descending:$Descending } foreach($dirName in $dirNames) { $dirPath = "$Path/$dirName" if ($Skip -and (& $Skip $dirPath)) { continue } $README = "$dirPath/README.md" # case: no README if (!(Test-Path -LiteralPath $README)) { if ($Level -lt $Depth -and (Get-ChildItem -LiteralPath $dirPath -Recurse -Filter README.md)) { # output the list line with the folder name '{0}- {1}' -f $indent, $dirName # process sub-folders Get-List $dirPath ($Level + 1) } elseif (!$NoWarning) { Write-Warning "Found no README in '$dirPath'." } continue } # the first README line is heading, get the topic text switch -File $README -Regex { '^#{1,6}(.*)' { $topic = $matches[1].Trim() break } default { throw "Expected first line heading in '$README'." } } # output the list line with the topic link '{0}- [{1}]({2})' -f $indent, $topic, $dirPath # process sub-folders if ($Level -lt $Depth) { Get-List $dirPath ($Level + 1) } } } ### main $Root = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($Root) if (!(Test-Path -LiteralPath $Root)) { throw "Cannot find '$Root'." } $README = "$Root/README.md" if (!(Test-Path -LiteralPath $README)) { throw "Cannot find '$README'." } $Mark = "<!--$Content-->" ### collect lines before and after marks $lines1 = [System.Collections.Generic.List[string]]@() $lines2 = [System.Collections.Generic.List[string]]@() $step = 1 foreach($_ in Get-Content -LiteralPath $README) { if ($step -eq 1) { $lines1.Add($_) if ($_ -eq $Mark) { ++$step } continue } if ($step -eq 2) { if ($_ -eq $Mark) { $lines2.Add($_) ++$step } continue } $lines2.Add($_) } if ($step -ne 3) { throw "Cannot find two marks '$Mark' in '$README'." } ### update README $( Push-Location -LiteralPath $Root try { $lines1 Get-List $Content 0 $lines2 } finally { Pop-Location } ) | Set-Content -LiteralPath $README -Encoding UTF8 |