201 lines
6.3 KiB
PowerShell
201 lines
6.3 KiB
PowerShell
param(
|
|
[string]$Root = (Get-Location).Path,
|
|
[string]$OutputPath,
|
|
[switch]$Quiet
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
if (-not (Test-Path -LiteralPath $Root)) {
|
|
throw "Root path does not exist: $Root"
|
|
}
|
|
|
|
$rootPath = (Resolve-Path -LiteralPath $Root).Path.TrimEnd([char[]]"\/")
|
|
|
|
if ([string]::IsNullOrWhiteSpace($OutputPath)) {
|
|
$OutputPath = Join-Path (Join-Path $rootPath "_indexes") "requirements-index.json"
|
|
}
|
|
|
|
function Get-RelativePath {
|
|
param([string]$Path)
|
|
|
|
$fullPath = (Resolve-Path -LiteralPath $Path).Path
|
|
if ($fullPath.StartsWith($rootPath, [System.StringComparison]::OrdinalIgnoreCase)) {
|
|
return ($fullPath.Substring($rootPath.Length).TrimStart([char[]]"\/") -replace "\\", "/")
|
|
}
|
|
|
|
return ($fullPath -replace "\\", "/")
|
|
}
|
|
|
|
function Get-FrontMatter {
|
|
param([string]$Content)
|
|
|
|
$match = [regex]::Match($Content, "(?s)\A---\r?\n(?<body>.*?)\r?\n---")
|
|
if ($match.Success) {
|
|
return $match.Groups["body"].Value
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
function Get-MetadataValue {
|
|
param(
|
|
[string]$FrontMatter,
|
|
[string]$Key
|
|
)
|
|
|
|
$match = [regex]::Match($FrontMatter, "(?m)^$([regex]::Escape($Key))\s*:\s*(?<value>.*)\s*$")
|
|
if ($match.Success) {
|
|
return $match.Groups["value"].Value.Trim().Trim('"').Trim("'")
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
function Get-MetadataList {
|
|
param(
|
|
[string]$FrontMatter,
|
|
[string]$Key
|
|
)
|
|
|
|
$lines = $FrontMatter -split "`r?`n"
|
|
$values = New-Object System.Collections.Generic.List[string]
|
|
|
|
for ($i = 0; $i -lt $lines.Count; $i++) {
|
|
if ($lines[$i] -match "^$([regex]::Escape($Key))\s*:\s*(?<inline>.*)\s*$") {
|
|
$inline = $Matches["inline"].Trim()
|
|
if ($inline.StartsWith("[") -and $inline.EndsWith("]")) {
|
|
$inline.Trim("[]").Split(",") |
|
|
ForEach-Object { $_.Trim().Trim('"').Trim("'") } |
|
|
Where-Object { -not [string]::IsNullOrWhiteSpace($_) } |
|
|
ForEach-Object { [void]$values.Add($_) }
|
|
}
|
|
|
|
for ($j = $i + 1; $j -lt $lines.Count; $j++) {
|
|
if ($lines[$j] -match "^\s*-\s*(?<value>.+?)\s*$") {
|
|
[void]$values.Add($Matches["value"].Trim().Trim('"').Trim("'"))
|
|
continue
|
|
}
|
|
|
|
if ($lines[$j] -match "^\S[^:]*\s*:") {
|
|
break
|
|
}
|
|
}
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
return @($values)
|
|
}
|
|
|
|
function Get-MarkdownSectionBody {
|
|
param(
|
|
[string]$Content,
|
|
[string]$Section
|
|
)
|
|
|
|
$match = [regex]::Match($Content, "(?ms)^##\s+$([regex]::Escape($Section))[ `t]*\r?\n(?<body>.*?)(?=^##\s+|\z)")
|
|
if ($match.Success) {
|
|
return $match.Groups["body"].Value
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
function ConvertTo-PlainText {
|
|
param([string]$Value)
|
|
|
|
if ([string]::IsNullOrWhiteSpace($Value)) {
|
|
return ""
|
|
}
|
|
|
|
return (($Value -replace "`r?`n", " ") -replace "\s+", " ").Trim()
|
|
}
|
|
|
|
function Get-PackageInfo {
|
|
param([string]$RelativePath)
|
|
|
|
$parts = $RelativePath -split "/"
|
|
$domain = ""
|
|
$packageType = ""
|
|
$slug = ""
|
|
|
|
if ($parts.Count -ge 4) {
|
|
$domain = $parts[0]
|
|
$packageType = $parts[1]
|
|
$slug = $parts[2]
|
|
}
|
|
|
|
return [pscustomobject]@{
|
|
domain = $domain
|
|
packageType = $packageType
|
|
slug = $slug
|
|
}
|
|
}
|
|
|
|
$excludedTopDirs = @(".git", "templates", "scripts")
|
|
$requirementFiles = Get-ChildItem -Path $rootPath -Recurse -File -Filter "requirement.md" -Force |
|
|
Where-Object {
|
|
$relativePath = Get-RelativePath $_.FullName
|
|
$topDir = ($relativePath -split "/")[0]
|
|
$excludedTopDirs -notcontains $topDir
|
|
}
|
|
|
|
$items = New-Object System.Collections.Generic.List[object]
|
|
|
|
foreach ($file in $requirementFiles) {
|
|
$relativePath = Get-RelativePath $file.FullName
|
|
$content = Get-Content -Raw -Encoding UTF8 -LiteralPath $file.FullName
|
|
|
|
if ($content -notmatch "(?m)^status:\s*requirement-draft\s*$") {
|
|
continue
|
|
}
|
|
|
|
$frontMatter = Get-FrontMatter -Content $content
|
|
$packageInfo = Get-PackageInfo -RelativePath $relativePath
|
|
$tags = @(Get-MetadataList -FrontMatter $frontMatter -Key "tags")
|
|
|
|
[void]$items.Add([pscustomobject][ordered]@{
|
|
schemaVersion = 1
|
|
type = "requirement-package"
|
|
title = Get-MetadataValue -FrontMatter $frontMatter -Key "title"
|
|
category = Get-MetadataValue -FrontMatter $frontMatter -Key "category"
|
|
tags = $tags
|
|
status = Get-MetadataValue -FrontMatter $frontMatter -Key "status"
|
|
updated = Get-MetadataValue -FrontMatter $frontMatter -Key "updated"
|
|
source = Get-MetadataValue -FrontMatter $frontMatter -Key "source"
|
|
domain = $packageInfo.domain
|
|
packageType = $packageInfo.packageType
|
|
slug = $packageInfo.slug
|
|
relativePath = $relativePath
|
|
fullPath = $file.FullName
|
|
feature = ConvertTo-PlainText (Get-MarkdownSectionBody -Content $content -Section "功能")
|
|
flow = ConvertTo-PlainText (Get-MarkdownSectionBody -Content $content -Section "流程")
|
|
dataTables = ConvertTo-PlainText (Get-MarkdownSectionBody -Content $content -Section "数据表")
|
|
dictionaries = ConvertTo-PlainText (Get-MarkdownSectionBody -Content $content -Section "字典")
|
|
businessRules = ConvertTo-PlainText (Get-MarkdownSectionBody -Content $content -Section "业务规则")
|
|
acceptance = ConvertTo-PlainText (Get-MarkdownSectionBody -Content $content -Section "验收")
|
|
portability = ConvertTo-PlainText (Get-MarkdownSectionBody -Content $content -Section "移植说明")
|
|
sourceEvidence = ConvertTo-PlainText (Get-MarkdownSectionBody -Content $content -Section "来源依据")
|
|
pending = ConvertTo-PlainText (Get-MarkdownSectionBody -Content $content -Section "待确认")
|
|
related = ConvertTo-PlainText (Get-MarkdownSectionBody -Content $content -Section "Related")
|
|
})
|
|
}
|
|
|
|
$outputDirectory = Split-Path -Parent $OutputPath
|
|
if (-not [string]::IsNullOrWhiteSpace($outputDirectory) -and -not (Test-Path -LiteralPath $outputDirectory)) {
|
|
New-Item -ItemType Directory -Path $outputDirectory -Force | Out-Null
|
|
}
|
|
|
|
$itemArray = @($items.ToArray())
|
|
$json = ConvertTo-Json -InputObject $itemArray -Depth 8
|
|
Set-Content -LiteralPath $OutputPath -Value $json -Encoding UTF8
|
|
|
|
if (-not $Quiet) {
|
|
Write-Host "Requirement index built. Indexed $($items.Count) package(s): $OutputPath"
|
|
}
|
|
|
|
|
|
|