Public/Merge-Psd.ps1
|
function Merge-Psd { <# .SYNOPSIS Merges files and maps in order. Later ones win. .DESCRIPTION Uses the same rules as '!merge': maps deep merge, everything else is replaced, and the merge operators (+=, =+, |=, -=, :=) work. -Merge and Merge in the schema give keys a default operator. A file brings its own chain and includes with it. References are worked out against the merged result. .PARAMETER InputObject Paths to files (a .ps1 with -Loader), or maps, in order. A generated class works too, if it was made from a map. A map can have '!name' = 'team', to read it as !<team::Key> and to name it in errors. .PARAMETER Key Only these top-level keys are returned. Everything is still merged, so references work. .PARAMETER Schema Checks the merged result, with the same messages as Import-Psd. Merge in it gives keys their default operator. .PARAMETER Include Maps from the caller, by name, read with !<name::Key>. See Import-Psd. .PARAMETER Loader Reads files that aren't .psd1, like .ps1 scripts. See Import-Psd. .PARAMETER Merge Default operators for keys written without one, by key path. See Import-Psd. .EXAMPLE Merge-Psd ./base.psd1, ./prod.psd1, @{ Tags = @('y') } .EXAMPLE Merge-Psd ./base.psd1, ./prod.psd1 -Merge @{ '**' = '+=' } #> [CmdletBinding()] [OutputType([System.Collections.Specialized.OrderedDictionary])] param( [Parameter(Mandatory, Position = 0)] [object[]] $InputObject, [string[]] $Key, [string] $Schema, [System.Collections.IDictionary] $Include, [System.Collections.IDictionary] $Merge, [scriptblock] $Loader, [ValidateSet('Error', 'Warn', 'Null', 'Keep')] [string] $UnresolvedReference = 'Error', [System.Collections.IDictionary] $Limits ) $ctx = New-PsdContext -Limits $Limits -Unresolved $UnresolvedReference if ($Loader) { $ctx.Loader = $Loader } $sources = New-PsdList $entry = $null $n = 0 foreach ($item in $InputObject) { $n++ if ($item -is [string] -or $item -is [IO.FileInfo]) { $f = Read-PsdFile (Resolve-PsdUserPath ([string]$item)) $ctx if ($null -eq $f -or $null -eq $f.Root) { continue } $sources.Add(@{ File = $f }) $entry = $f } elseif (Test-PsdMapLike $item) { $sf = New-PsdSyntheticFile "input $n" $null $sf.Root = ConvertFrom-PsdObject $item $sf $ctx '' -AllowName $sources.Add(@{ Layer = (New-PsdLayer $sf) }) if ($sf.Named) { $entry = $sf } } else { Add-PsdError $ctx 'PSD206' "input $n is $(Get-PsdKindText $item). Merge-Psd takes paths and maps." } } if ($null -eq $entry) { $entry = New-PsdSyntheticFile 'merge' $null; $entry.Root = New-PsdMapNode $entry $null '' } # the schema is read before merging: Merge in it gives keys their default operator $rules = $null if ($Schema -and $ctx.Errors.Count -eq 0) { $rules = Import-PsdSchemaRules (Resolve-PsdUserPath $Schema) $ctx $null } $defaults = ConvertFrom-PsdMergeOption $Merge $ctx $value = $null if ($sources.Count -gt 0 -and $ctx.Errors.Count -eq 0) { $doc = New-PsdDoc $entry $sources $ctx $false $Include $rules $defaults if ($Key) { $have = New-PsdList foreach ($l in $doc.Layers) { foreach ($x in (Get-PsdTopKeys $l.File.Root)) { $have.Add($x) } } foreach ($k in $Key) { if ($have -inotcontains $k) { Add-PsdError $ctx 'PSD207' "'$k' isn't a key in any of the inputs." -Hint @((Get-PsdDidYouMean $k $have)) } } # Everything is merged, so references still work. Only these keys are returned. $value = [ordered]@{} foreach ($k in $Key) { foreach ($have in $doc.Tree.V.PSBase.Keys) { if ($have -ieq $k) { $value[$have] = Get-PsdNodeValue $doc.Tree.V[$have] $doc } } } } else { $value = Resolve-PsdDoc $doc } if ($null -ne $rules -and $ctx.Errors.Count -eq 0) { $whole = Resolve-PsdDoc $doc if ($ctx.Errors.Count -eq 0) { Test-PsdDocSchema $whole $doc $rules $ctx } } } Write-PsdResult $PSCmdlet $ctx 'the inputs' if ($ctx.Errors.Count -eq 0 -and $null -ne $value) { ConvertTo-PsdOutput $value } } |