Types/OpenPackage.Source/Dictionary.ps1

<#
.SYNOPSIS
    Gets a Dictionary as a Package
.DESCRIPTION
    Gets a dictionary as an Open Package.
.NOTES
    Keys in the dictionary will treated as file names.

    If the value is a dictionary or list of dictionaries,
    the key will become a directory name and its values will be added to the package.
#>


param(
[Alias('Dictionary')]
[Collections.IDictionary[]]
$DictionaryList,

# The compression option.
[IO.Packaging.CompressionOption]
[Alias('CompressionLevel')]
$CompressionOption = 'Superfast',

# A content type map.
# This maps extensions and URIs to a content type.
[Collections.IDictionary]
$TypeMap = $(
    ([PSCustomObject]@{PSTypeName='OpenPackage.ContentTypeMap'}).TypeMap
),

# The base path within the package.
# Content should be added beneath this base path.
[string]
$BasePath = '/',

# If set, will force the redownload of various resources and remove existing files or directories
[switch]
$Force,

# The current package.
# If a package is provided, will add content to this package.
# If no package is provided, will create a new package.
[IO.Packaging.Package]
$Package
)

$BasePath = $BasePath -replace '^/?', '/' -replace '/?$', '/'

$myself = $MyInvocation.MyCommand.ScriptBlock

foreach ($dictionary in $dictionaryList) {
    if ($dictionary -isnot [Collections.IDictionary]) { continue }    
    if (-not $package) {
        $memoryStream = [IO.MemoryStream]::new()
        $package = [IO.Packaging.Package]::Open($memoryStream, 'OpenOrCreate', 'ReadWrite')
        $package.PackageProperties.Identifier = $resolvedItem.Name
        Add-Member -InputObject $package NoteProperty MemoryStream $memoryStream -Force
    }            
    :nextPart foreach ($key in $dictionary.Keys) {
        $value = $dictionary[$key]
        $partUri = ($BasePath + $key) -replace '//', '/'
        if ($value -is [Collections.IDictionary] -or 
            $value -as [Collections.IDictionary[]]
        ) {
            $Package = & $myself -DictionaryList $value -BasePath $partUri -Package $Package
        } else {                    
            $newPart = if ($Package.PartExists -and 
                $Package.PartExists($partUri)
            ) {
                if (-not $Force) {
                    Write-Warning "$PartUri already exists, use -Force to overwrite"
                    continue nextPart
                }
                $existingPart = $Package.GetPart($partUri)
                $partContentType = $existingPart.ContentType
                $Package.DeletePart($partUri)
                $Package.CreatePart($partUri, $partContentType, $CompressionOption)
            } else {
                $partContentType = 
                    if ($partUri -match '\.[^\.]+?$' -and $TypeMap -and 
                        $TypeMap[$matches.0]) {
                        $TypeMap[$matches.0]
                    } else {
                        if ($value -as [byte[]]) {
                            "application/octet-stream"
                        } else {
                            "text/plain"
                        }
                    }
                $Package.CreatePart($partUri, $partContentType, $CompressionOption)                                                
            }

            if (-not $newPart) {
                continue nextPart
            }

            $newStream = $newPart.GetStream()
            
            if ($value -is [xml]) {
                $value.Save($newStream)
            }
            elseif ($partUri -match '\.json$') {
                if ($value -isnot [string] -and $value -notmatch '[^\[\{]') {
                    $json = $value | ConvertTo-Json -Depth $FormatEnumerationLimit
                    $buffer = $OutputEncoding.GetBytes("$json")
                    $newStream.write($buffer, 0, $buffer.Length)
                } else {
                    $value.Save($newStream)
                }
            }
            elseif ($partUri -match '\.clixml$') {
                $clixml = [Management.Automation.PSSerializer]::Serialize($value)
                $buffer = $OutputEncoding.GetBytes($clixml)
                $newStream.write($buffer, 0, $buffer.Length)
            }
            else {
                if ($value -is [byte[]]) {                            
                    $newStream.write($value, 0, $value.Length)
                }
                else {
                    $buffer = $OutputEncoding.GetBytes("$value")
                    $newStream.write($buffer, 0, $buffer.Length)
                }
            }

            $newStream.Close()
            $newStream.Dispose()
        }
    }
    $Package                
}