Private/StandardInheritance.ps1
|
# Resolve standards without evaluating code or depending on the caller's folder. function Find-SLStandardPath { param([string] $Directory = (Get-Location).ProviderPath) $folder = [IO.DirectoryInfo] $Directory while ($null -ne $folder) { $candidate = Join-Path $folder.FullName 'secondline.standard.psd1' if (Test-Path -LiteralPath $candidate -PathType Leaf) { return $candidate } $folder = $folder.Parent } return $null } function Read-SLStandardData { param([string] $Path, [string[]] $Stack = @()) $resolved = (Resolve-Path -LiteralPath $Path -ErrorAction Stop).ProviderPath if ($Stack -contains $resolved) { throw "Circular SecondLine inheritance: $(@($Stack + $resolved) -join ' -> ')" } if ($Stack.Count -ge 64) { throw 'SecondLine inheritance exceeds 64 files.' } try { $data = Import-PowerShellDataFile -LiteralPath $resolved -ErrorAction Stop } catch { throw "Could not read $resolved as a PowerShell data file: $($_.Exception.Message)" } if (-not $data.ContainsKey('Extends')) { return $data } if ($data.Extends -isnot [string] -or -not $data.Extends.Trim()) { throw "SecondLine standard $resolved requires Extends to be a nonempty string." } $parent = $data.Extends if ($parent -eq '@development') { $parent = Join-Path (Split-Path $PSScriptRoot -Parent) 'Standards\development.psd1' } elseif (-not [IO.Path]::IsPathRooted($parent)) { $parent = Join-Path (Split-Path $resolved -Parent) $parent } if (-not (Test-Path -LiteralPath $parent -PathType Leaf)) { throw "SecondLine parent standard not found: $parent (from $resolved)." } $base = Read-SLStandardData -Path $parent -Stack @($Stack + $resolved) $merged = @{} foreach ($key in $base.Keys) { $merged[$key] = $base[$key] } foreach ($key in $data.Keys) { if ($key -notin @('Tools', 'GitAttributes', 'Extends')) { $merged[$key] = $data[$key] } } # Child entries replace complete tools by Name, preserving distinct inherited tools. $tools = @($base.Tools) if ($null -eq $base.Tools) { $tools = @() } foreach ($tool in @($data.Tools)) { if ($null -eq $tool) { continue } if ($tool -is [hashtable] -and $tool.Name) { $tools = @($tools | Where-Object { $_.Name -ne $tool.Name }) } $tools += $tool } if ($base.ContainsKey('Tools') -or $data.ContainsKey('Tools')) { $merged.Tools = $tools } if ($data.ContainsKey('GitAttributes')) { if ($data.GitAttributes -isnot [hashtable] -or $base.GitAttributes -isnot [hashtable]) { $merged.GitAttributes = $data.GitAttributes } else { $section = @{} foreach ($key in $base.GitAttributes.Keys) { $section[$key] = $base.GitAttributes[$key] } foreach ($key in $data.GitAttributes.Keys) { if ($section[$key] -is [hashtable] -and $data.GitAttributes[$key] -is [hashtable]) { $values = @{} foreach ($entry in $section[$key].Keys) { $values[$entry] = $section[$key][$entry] } foreach ($entry in $data.GitAttributes[$key].Keys) { $values[$entry] = $data.GitAttributes[$key][$entry] } $section[$key] = $values } else { $section[$key] = $data.GitAttributes[$key] } } $merged.GitAttributes = $section } } return $merged } |