Private/Invoke-MultiTfmMerge.ps1

function Invoke-MultiTfmMerge {
<#
.SYNOPSIS
    Merges the lib/ TFM entries from a net6 nupkg into a net8 nupkg,
    producing a single multi-targeted nupkg. For Library projects only.
 
.DESCRIPTION
    1. Extracts the net8 nupkg to a temp directory.
    2. Reads the net6 nupkg and copies lib/net6* entries into the temp directory.
    3. Patches the nuspec: adds a net6.0-windows7.0 dependency group cloned from
       the net8.0-windows7.0 group (minus System.Activities.ViewModels).
    4. Re-packs the temp directory into OutputPath.
    5. Cleans up the temp directory in a finally block.
 
.PARAMETER Net8Nupkg
    Full path to the net8-built .nupkg file.
 
.PARAMETER Net6Nupkg
    Full path to the net6-built .nupkg file.
 
.PARAMETER OutputPath
    Full path for the merged .nupkg output file.
#>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([string])]
    param(
        [Parameter(Mandatory)]
        [string]$Net8Nupkg,

        [Parameter(Mandatory)]
        [string]$Net6Nupkg,

        [Parameter(Mandatory)]
        [string]$OutputPath
    )

    Set-StrictMode -Version Latest
    $ErrorActionPreference = 'Stop'

    if (-not (Test-Path $Net8Nupkg)) { throw "Net8 nupkg not found: $Net8Nupkg" }
    if (-not (Test-Path $Net6Nupkg)) { throw "Net6 nupkg not found: $Net6Nupkg" }

    if (-not $PSCmdlet.ShouldProcess($OutputPath, 'Merge net6 and net8 TFMs into single nupkg')) { return }

    Add-Type -AssemblyName System.IO.Compression.FileSystem

    $tempDir = Join-Path ([System.IO.Path]::GetTempPath()) "uipack-merge-$([guid]::NewGuid().ToString('N').Substring(0,8))"

    try {
        # 1. Extract net8 nupkg
        [System.IO.Compression.ZipFile]::ExtractToDirectory($Net8Nupkg, $tempDir)

        # 2. Copy lib/net6* entries from net6 nupkg
        $zip = [System.IO.Compression.ZipFile]::OpenRead($Net6Nupkg)
        try {
            $net6Entries = $zip.Entries | Where-Object {
                $_.FullName -match '^lib/net6' -and $_.Length -gt 0
            }
            foreach ($entry in $net6Entries) {
                $destPath = Join-Path $tempDir ($entry.FullName -replace '/', '\')
                $destDir  = Split-Path $destPath -Parent
                if (-not (Test-Path $destDir)) {
                    $null = New-Item -ItemType Directory -Path $destDir -Force
                }
                [System.IO.Compression.ZipFileExtensions]::ExtractToFile($entry, $destPath, $true)
            }
            if ($net6Entries) {
                $tfmName = ($net6Entries | Select-Object -First 1).FullName -replace '^lib/([^/]+)/.*', '$1'
                Write-Verbose "[MultiTfm] Merged lib/$tfmName/ ($($net6Entries.Count) files) from net6 build"
            } else {
                Write-Warning "[MultiTfm] net6 nupkg had no lib/net6* entries — merged nupkg will be net8 only"
            }
        } finally {
            $zip.Dispose()
        }

        # 3. Patch nuspec: add net6.0-windows7.0 dependency group
        $nuspecPath = Get-ChildItem -Path $tempDir -Filter '*.nuspec' | Select-Object -First 1 -ExpandProperty FullName
        if ($nuspecPath) {
            [xml]$nuspec = Get-Content $nuspecPath -Raw -Encoding UTF8
            $ns    = $nuspec.DocumentElement.NamespaceURI
            $nsMgr = New-Object System.Xml.XmlNamespaceManager($nuspec.NameTable)
            $nsMgr.AddNamespace('nu', $ns)

            $depsNode = $nuspec.SelectSingleNode('//nu:dependencies', $nsMgr)
            if ($depsNode) {
                $existingNet6 = $depsNode.SelectSingleNode("nu:group[@targetFramework='net6.0-windows7.0']", $nsMgr)
                if (-not $existingNet6) {
                    $net8Group = $depsNode.SelectSingleNode("nu:group[@targetFramework='net8.0-windows7.0']", $nsMgr)
                    if ($net8Group) {
                        $net6Group = $net8Group.CloneNode($true)
                        $net6Group.SetAttribute('targetFramework', 'net6.0-windows7.0')
                        # Remove net8-only dependency
                        $viewModelsDep = $net6Group.SelectSingleNode("nu:dependency[@id='System.Activities.ViewModels']", $nsMgr)
                        if ($viewModelsDep) {
                            $net6Group.RemoveChild($viewModelsDep) | Out-Null
                        }
                        $depsNode.AppendChild($net6Group) | Out-Null

                        $utf8NoBom = New-Object System.Text.UTF8Encoding($false)
                        $writer    = New-Object System.IO.StreamWriter($nuspecPath, $false, $utf8NoBom)
                        try { $nuspec.Save($writer) } finally { $writer.Close() }
                        Write-Verbose "[MultiTfm] Added net6.0-windows7.0 dependency group to nuspec"
                    }
                }
            }
        }

        # 4. Re-pack into OutputPath
        $null = New-Item -ItemType Directory -Path (Split-Path $OutputPath -Parent) -Force
        if (Test-Path $OutputPath) { Remove-Item $OutputPath -Force }
        [System.IO.Compression.ZipFile]::CreateFromDirectory(
            $tempDir,
            $OutputPath,
            [System.IO.Compression.CompressionLevel]::Optimal,
            $false
        )
        Write-Verbose "[MultiTfm] Created merged nupkg: $(Split-Path $OutputPath -Leaf)"

        Write-Output $OutputPath

    } finally {
        if (Test-Path $tempDir) {
            Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
        }
    }
}