Commands/Get-OpenPackage.ps1

function Get-OpenPackage
{
    <#
    .SYNOPSIS
        Gets Open Packages
    .LINK
        https://en.wikipedia.org/wiki/Open_Packaging_Conventions
    .INPUTS
        Almost anything
    .OUTPUTS
        Open Packages
    .DESCRIPTION
        Gets Open Packages from almost anything.

        Anything can be a package.

        This command helps you make anything into a package.

        The following types of packages are currently supported:

        * Any [Open Packaging Convention](https://en.wikipedia.org/wiki/Open_Packaging_Conventions) files
        * Any directory
        * Any `*.zip` file
        * Any `*.tar.gz` file
        * Any url
        * Any public nuget package
        * Any git repository
        * Any public at protocol URI
        * Any dictionary (including nested dictionaries)
        * Any single file

        Anything can be a package.
        
        Once we start to treat anything as a package, we can do amazing things with packages.

        Like:

        * Inspect any packages before we work with them.
        * Modify the packages to customize their content.
        * Split packages
        * Filter our components.
        * Join them back together.
        * Search package content.
        * Work with compressed trees of data.
        * Have an in-memory containerized virtual filesystem.
        * Serve a package from memory.
        * Store data to N package layers.
    .EXAMPLE
        # Make the current directory into a package
        # (do not try this at `$home`)
        Get-OpenPackage .
    .EXAMPLE
        # Make the module into a package
        $opPackage = Get-Module OP | Get-OpenPackage
    .EXAMPLE
        $opPackage = Get-Module OP |
            Get-OpenPackage -Include *.ps1
    .EXAMPLE
        # Another way to make the current directory into a package
        # (do not try this at `$home`)
        Get-Item . | Get-OpenPackage
    .EXAMPLE
        # Get a package from nuget
        $Avalonia = OP https://www.nuget.org/packages/Avalonia/
    .EXAMPLE
        # Get a package from Chocolatey
        $chocoPackage = op https://community.chocolatey.org/packages/chocolatey
    .EXAMPLE
        # Get a package from the PowerShell gallery
        $turtlePackage = op https://powershellgallery.com/packages/Turtle
    .EXAMPLE
        # Get a package from a single URL
        $imagePackage = op https://MrPowerShell.com/MrPowerShell.png
    .EXAMPLE
        # Create a package from multiple URLs by piping back to ourself
        $svgAndPng = op https://MrPowerShell.com/MrPowerShell.png |
            op https://MrPowerShell.com/MrPowerShell.svg
    .EXAMPLE
        # Get a package from an at protocol URI
        $atPost = op at://mrpowershell.com/app.bsky.feed.post/3k4hf5dy6nf2g
    .EXAMPLE
        # Get the most recent 50 posts
        $atLast50 = op at://mrpowershell.com/app.bsky.feed.post/ -First 50
    .EXAMPLE
        # Get all standard.site.documents for a user
        $standardSiteDocuments = op at://mrpowershell.com/site.standard.document/
    .EXAMPLE
        $MrPowerShellStandardSite = op @(
            'at://mrpowershell.com/site.standard.document/'
            'at://mrpowershell.com/site.standard.publication/'
        )
    .EXAMPLE
        $MrPowerShellStrings = op 'at://mrpowershell.com/sh.tangled.string/'
    .EXAMPLE
        # Get [feather icons](https://feathericons.com/) as an open package.
        $featherIcons = Get-OpenPackage https://github.com/feathericons/feather/tree/main/icons
    .EXAMPLE
        # Get the [itermColorSchemes](https://iterm2colorschemes.com/) for windows terminal
        $iTermPalettes =
            op 'https://github.com/mbadolato/iTerm2-Color-Schemes/blob/master/windowsterminal/'
    #>

    [CmdletBinding(PositionalBinding=$false,DefaultParameterSetName='Any',SupportsPaging)]
    [Alias(
        'Get-OP', 'OP', 'OpenPackage','gOpenPackage',
        'Open-OpenPackage', 'Open-OP', 'opop', 'opOpenPackage'
    )]
    param(
    # Any unnamed arguments to the command.
    # Each argument will be treated as a potential -FilePath or -Uri.
    # Once the first related verb is detected, these will become arguments to that verb
    # (For example, `op . start` will get an open package and then start a server for that package)
    [Parameter(ValueFromRemainingArguments)]
    [PSObject[]]
    $ArgumentList,

    # The path of a file to import
    [Parameter(Mandatory,ParameterSetName='FilePath',ValueFromPipelineByPropertyName)]
    [Alias('Fullname')]
    [string]
    $FilePath,

    # Gets Open Packages with `@` syntax.
    # Without a domain, `@` will be presumed to be a github
    # With a domain, `@` will look for an https url, and check at protocol
    [Parameter(Mandatory,ParameterSetName='At',ValueFromPipelineByPropertyName)]
    [string[]]
    $At,

    # A URI to package.
    # If this URI is a git repository, will make a package out of the repository
    # If this URI is a nuget package url or powershell gallery url, will download the package.
    [Parameter(Mandatory,ParameterSetName='Uri',ValueFromPipelineByPropertyName)]
    [Alias('Url')]
    [uri[]]
    $Uri,

    # Any additional headers to pass into a web request.
    [Alias('Header')]
    [Collections.IDictionary]
    $Headers = [Ordered]@{},

    # A Repository to package.
    # This can be the root of a repo or a link to a portion of the tree.
    # If a portion of the tree is provided, will perform a sparse clone of the repository
    [Parameter(Mandatory,ParameterSetName='Repository',ValueFromPipelineByPropertyName)]
    [Alias('clone_url')]
    [string]
    $Repository,

    # The github branch name.
    [Parameter(ValueFromPipelineByPropertyName)]
    [string]
    $Branch,

    # One or more optional sparse filters to a repository.
    # If these are provided, only files matching these filters will be downloaded.
    [Parameter(ValueFromPipelineByPropertyName)]    
    [string[]]
    $SparseFilter,

    # An At Uri to package.
    # This can be a single post or a collection of all posts of a type.
    [Parameter(Mandatory,ParameterSetName='AtUri',ValueFromPipelineByPropertyName)]
    [string[]]
    $AtUri,

    # The personal data server. This is used in At Protocol requests.
    [Parameter(ValueFromPipelineByPropertyName)]
    [string]
    $PDS,

    # Adds a dictionary of content to the package
    [Parameter(Mandatory,ParameterSetName='Dictionary',ValueFromPipelineByPropertyName)]
    [Collections.IDictionary]
    $Dictionary,

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

    # A Nuget Uri to package.
    # The package at this location will be downloaded and opened directly.
    [Parameter(Mandatory,ParameterSetName='NugetPackage',ValueFromPipelineByPropertyName)]
    [Alias('NugetPackage','PowerShellGallery','ChocolateyGallery')]
    [uri]
    $NuGet,

    # One or more Node Packages.
    [Parameter(Mandatory,ParameterSetName='NodePackage',ValueFromPipelineByPropertyName)]
    [Alias('npm','npmx')]
    [string[]]
    $NodePackage,

    # One or more Python Packages.
    [Parameter(Mandatory,ParameterSetName='PythonPackage',ValueFromPipelineByPropertyName)]
    [Alias('pip','whl')]
    [string[]]
    $PythonPackage,


    # A module to package
    # A loaded module name or moduleinfo object to package.
    # The loaded module must have a path property.
    # The files in this path will be packaged.
    [Parameter(Mandatory,ParameterSetName='Module',ValueFromPipelineByPropertyName)]
    [PSObject]
    $Module,
    
    # A list of file wildcards to include.
    [Parameter(ValueFromPipelineByPropertyName)]
    [SupportsWildcards()]
    [string[]]
    $Include,

    # A list of file wildcards to exclude.
    [Parameter(ValueFromPipelineByPropertyName)]
    [SupportsWildcards()]
    [string[]]
    $Exclude,

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

    # The compression option.
    [IO.Packaging.CompressionOption]
    [Alias('CompressionLevel')]
    $CompressionOption = 'Superfast',
    
    # One or more input objects.
    [Parameter(ValueFromPipeline)]
    [Alias('Package')]
    [PSObject]
    $InputObject,
    
    # Gets the packages that are currently installed
    [Parameter(Mandatory,ParameterSetName='Installed')]
    [switch]
    $Installed,

    # Gets packages that are currently running in a server
    [Parameter(Mandatory,ParameterSetName='Running')]
    [switch]
    $Running,
    
    # If set, will force the redownload of various resources and remove existing files or directories
    [switch]
    $Force,

    # If set, will include hidden files and folders, except for files beneath `.git`
    [Alias('IncludeDotFiles')]
    [switch]
    $IncludeHidden,

    # If set, will include the `.git` directory contents if found.
    # By default, this content will be excluded.
    [Alias('IncludeGitFile','IncludeGitFiles','IncludeGitDirectory')]
    [switch]
    $IncludeGit,

    # If set, will include any content found in `/node_modules`.
    # By default, this content will be excluded.
    [Alias('IncludeNodeModules')]
    [switch]
    $IncludeNodeModule,

    # If set, will include any content found in `/_site`.
    # By default, this content will be excluded.
    [Alias('IncludeWebsite')]
    [switch]
    $IncludeSite
    )

    begin {        
        # First, set output encoding to UTF8
        # This should ensure things work consistently regardless of operating system and user preference.
        $OutputEncoding = [Text.Encoding]::UTF8
        
        # And we want to keep track of our command name, so we can semi-anonymously recurse.
        $myCommandName = $MyInvocation.MyCommand.Name

        # Next up we initialize a type map.
        # This maps extensions and uris to a content type, and is used when creating parts.
        $typeMap = ([PSCustomObject]@{PSTypeName='OpenPackage.ContentTypeMap'}).TypeMap
                
        # Gets the open package type data, as we will need this
        $OpTypeData = Get-TypeData -TypeName OpenPackage.Source
        
        function InvokeOpMethod {
            param([string]$Name, [Collections.IDictionary]$Parameter)

            if (-not $OpTypeData.Members[$Name].Script) {
                return
            }
            $bindableParameters = [Ordered]@{}
            $function:func = $OpTypeData.Members[$Name].Script
            $func = $ExecutionContext.SessionState.InvokeCommand.GetCommand('func', 'Function')
            
            :nextParameter foreach ($parameterName in $func.Parameters.Keys) {
                if ($null -ne $parameter[$parameterName]) {
                    $bindableParameters[$parameterName] = $Parameter[$parameterName]
                    continue nextParameter
                }
                foreach ($aliasName in $func.Parameters[$parameterName].Aliases) {
                    if ($null -ne $Parameter[$aliasName]) {
                        $bindableParameters[$aliasName] = $Parameter[$aliasName]
                        continue nextParameter
                    }
                }
            }

            $ExecutionContext.SessionState.PSVariable.Remove('function:func')
            
            try {                
                & $OpTypeData.Members[$Name].Script @bindableParameters
            }  catch {
                $PSCmdlet.WriteError($_)
            }            
        }
        

        # The full default type map is much more robust.
        # If this was being used without a module context, we still want to work for _most_ scenarios
        # So this a secondary default, declared inline, that captures a fairly short list of common content types.
        if (-not $typeMap -or -not $typeMap.Count) {
            $typeMap = [Ordered]@{
                ".css" = "text/css"; ".html" = "text/html"; 
                ".svg" = "image/svg+xml"; '.js' = 'text/javascript';".jsm" = "text/javascript";
                ".png" = "image/png"; ".gif" = "image/gif";
                ".jpg" = "image/jpeg"; ".jpeg" = "image/jpeg"; 
                ".md" = "text/markdown"
                ".mp3" = "audio/mpeg"; ".mp4" = "video/mp4";
                ".xml" = "application/xml"
            }
        }

        # Now declare several internal filters to turn various things into packages.
        
        # These are in alphabetical order, not in order of likely use.
        filter getCurrentPack {
            # Gets the current package

            # If the input object was a package, get it
            if ($inputObject -is [IO.Packaging.Package]) {
                $currentPackage = $InputObject                
            } else {
                # Otherwise,
                $memoryStream = [IO.MemoryStream]::new()
                $currentPackage = [IO.Packaging.Package]::Open($memoryStream, 'OpenOrCreate','ReadWrite')
                Add-Member NoteProperty MemoryStream $memoryStream -Force -InputObject $currentPackage
            }
            
            $currentPackage
        }
        
        filter packFile {
            # If we are creating a package from a file
            $resolvedItem = $_
            
            $peekMagicBytes =  Get-Content -AsByteStream -LiteralPath $resolvedItem.FullName -First 5
            
            if ($peekMagicBytes[0,1] -as 'char[]' -join '' -eq 'PK') {
                return $resolvedItem.FullName | packZip
            }
            elseif (
                $peekMagicBytes[0,1] -as 'char[]' -join '' -eq './'
            ) {
                return $resolvedItem | packTar
            }
            elseif ($peekMagicBytes[0] -eq 31 -and 
                $peekMagicBytes[1] -eq 139 -and
                $peekMagicBytes[2] -eq 8
            ) {
                return $resolvedItem | packTar
            }
                                            
            # Read the file content
            $fileBytes = Get-Content -AsByteStream -Raw -LiteralPath $resolvedItem.FullName
            
            # Get our current package
            $currentPackage = getCurrentPack
            
            # Make the file name an encoded URI
            $relativeUri = [Web.HttpUtility]::UrlEncode($resolvedItem.Name)
            $relativeUri = '/' + ($relativeUri -replace '^/')                
            # $relativeUri = $relativeUri -replace '\s', '%20'

            # If the package has no identifier,
            if (-not $currentPackage.PackageProperties.Identifier) {
                # set it to the file name
                $currentPackage.PackageProperties.Identifier = $resolvedItem.Name
            }
            
            $currentPackage.PackageProperties.Identifier = $resolvedItem.Name
            
            # And determine the right extension content type
            
            $fileContentType = $typeMap[$resolvedItem.Extension]
            
            # and create a part
            $newPart =  $currentPackage.CreatePart($relativeUri, $fileContentType, $CompressionOption)
            if (-not $newPart) {
                continue
            }
            # then write the file to the part
            $newStream = $newPart.GetStream()
            $newStream.Write($fileBytes, 0, $fileBytes.Length)
            $newStream.Close()

            # and set the package properties based off of the resolved info.
            $currentPackage.PackageProperties.Created = $resolvedItem.CreationTime
            $currentPackage.PackageProperties.Modified = $resolvedItem.LastWriteTime
            $currentPackage
        }        

        filter packTar {
            $namedParameters['TarFile'] = $_
            InvokeOpMethod 'Tar' $NamedParameters
        }        

        filter packZip {
            $namedParameters['ZipFile'] = $_
            InvokeOpMethod 'Zip' $NamedParameters
        }

        $generateEvent = [Runspace]::DefaultRunspace.Events.GenerateEvent
    }

    process {                
        $namedParameters = [Ordered]@{} + $PSBoundParameters
        if ($PSCmdlet.PagingParameters.First -lt 1mb) {
            $namedParameters['First'] = $psCmdlet.PagingParameters.First
        }
        if ($PSCmdlet.PagingParameters.Skip -lt 1mb) {
            $namedParameters['Skip'] = $psCmdlet.PagingParameters.Skip
        }
        $messageData = [Ordered]@{} + $namedParameters
        # Generate an event
        $getOpenPackageEvent = $generateEvent.Invoke(
            'Get-OpenPackage', # for Get-OpenPackage
            $MyInvocation.MyCommand, # sent by this command
            @(
                # containing MyInvocation and MessageData
                $MyInvocation, $messageData
            ),
            # And sending the message data dictionary along
            $messageData,
            # process in the current thread
            $true, 
            # and wait for completion.
            $true
        )

        # If the event was processed, and they said any form of "no"
        if ($getOpenPackageEvent.MessageData.Rejected -or
            $getOpenPackageEvent.MessageData.Reject -or
            $getOpenPackageEvent.MessageData.No -or
            $getOpenPackageEvent.MessageData.Deny
        ) {
            Write-Warning "Will not $($MyInvocation.Line)"
            return
        }        
                    
        if ($InputObject) {
            $namedParameters.Remove('InputObject')
            switch ($InputObject) {
                {$_ -is [Management.Automation.PSModuleInfo] -and $_.Path} {
                    & $myCommandName -Module $InputObject @namedParameters
                    return
                }
                {$_ -is [IO.FileInfo] -or $_ -is [IO.DirectoryInfo]} {
                    & $myCommandName -FilePath $InputObject.FullName @namedParameters
                    return
                }
                {$_ -is [Collections.IDictionary]} {
                    & $myCommandName -Dictionary $InputObject @namedParameters
                }
                {$_ -is [uri]} {
                    & $myCommandName -Uri $InputObject @namedParameters
                }
            }
            $namedParameters.InputObject = $InputObject
        }

        # If arguments were provided, we are trying to be as natural with syntax as we can.
        # Each string can map to a parameter, or to a verb of a related command to run.
        if ($ArgumentList) {
            $packages = @()
            $loadedModules = @(Get-Module)
            $outputPackages = $true
            $namedParameters.Remove('ArgumentList')

            # Walk over each argument
            :nextArgument for ($argNumber = 0; $argNumber -lt $ArgumentList.Length; $argNumber++) {
                # If we have already output a package
                if ($packages -and
                    # but it was not explicitly mapped
                    -not $namedParameters.'package' -and
                    $packages[0] -is [IO.Packaging.Package]
                ) {
                    # map the package to the named parameters.
                    $namedParameters.'package' = $packages[0]
                }

                $arg = $ArgumentList[$argNumber]
                if ($arg -is [IO.Packaging.Package]) {
                    if ($packages -notcontains $arg) {
                        $packages += $arg
                    }
                    continue nextArgument
                }
                # If it is a string, path info, or uri
                if ($arg -is [Management.Automation.PSModuleInfo]) {
                    $modulePackage = Get-OpenPackage -Module $arg @namedParameters
                    if ($packages -notcontains $modulePackage) {
                        $packages += $modulePackage
                    }                     
                    # and continue to the next argument.
                    continue nextArgument
                }                

                if ($arg -is [Collections.IDictionary]) {
                    $packages += & $myCommandName -Dictionary $arg @namedParameters
                    continue nextArgument
                }
                
                if ($arg -is [string] -or 
                    $arg -is [Management.Automation.PathInfo] -or
                    $arg -is [uri]
                ) {
                    # see if the path exists
                    $slashKey = $arg -replace '^\.?/?', '/' -replace '/$'
                    
                    # If the input was a package, and it exists in the package
                    if (
                        $InputObject -is [IO.Packaging.Package] -and 
                        $InputObject.PartExists($slashKey)
                    ) {
                        # read the contents of the part and emit them to output
                        Get-OpenPackage -Uri $slashKey @namedParameters -InputObject $InputObject
                        # and continue to the next argument.
                        continue nextArgument
                    }
                    # If the argument is a path
                    if (Test-Path $arg -ErrorAction Ignore) {
                        # turn it into a package.
                        $filePackage = Get-OpenPackage -FilePath $arg @namedParameters
                        if ($packages -notcontains $filePackage) {
                            $packages += $filePackage
                        }
                                                
                        continue nextArgument
                    }
                    # If the argument started with `@`
                    if ($arg -match '^@') {
                        # treat it as open-ended at syntax
                        $atPackages = Get-OpenPackage -At $arg @namedParameters
                        foreach ($atPackage in $atPackages) {
                            if ($atPackage -isnot [IO.Packaging.Package]) {
                                continue
                            }
                            if ($packages -notcontains $atPackage) {
                               $packages += $atPackage    
                            }
                        }                                                
                        continue nextArgument
                    }                    
                    # If the argument started with `at://`
                    if ($arg -match '^at://') {
                        # treat it as an at uri and turn it into a package.
                        $atPackage = Get-OpenPackage -AtUri $arg @namedParameters
                        if ($packages -notcontains $atPackage) {
                            $packages += $atPackage    
                        }                        
                        continue nextArgument
                    }
                    # If the argument could be a URI
                    $argUri = $arg -as [uri]
                    # and that URI is absolute
                    if ($argUri.IsAbsoluteUri) {
                        # get that uri as a package
                        $uriPackage = Get-OpenPackage -Uri $argUri @namedParameters
                        if ($packages -notcontains $uriPackage) {
                            $packages += $uriPackage
                        }
                        continue nextArgument
                    }
                    
                    # The the argument is a string and the name of a loaded module
                    if ($arg -is [string] -and 
                        $loadedModules.Name -contains $arg) {
                        $packages += Get-OpenPackage -Module $arg @namedParameters
                        continue nextArgument
                    }
                                        
                    continue nextArgument
                }                
            }

            # If we did not run any additional commands,
            if ($outputPackages) {            
                $packages # we want to output our packages now
            }

            return # and return.
        }

        if ($inputObject -is [IO.Packaging.Package]) {
            $namedParameters['Package'] = $InputObject
        }

        if ($Installed) {
            if (-not $env:OpenPackagePath) {
                Write-Error '$env:OpenPackagePath not defined'
                return
            }

            Get-ChildItem -Path ($env:OpenPackagePath -split $(
                if ($isLinux -or $IsMacOs) {
                    ':'
                } else {
                    ';'
                }
            )) -ErrorAction Ignore
            return
        }

        if ($running) {
            Get-Job | 
                Where-Object {
                    $job = $_
                    if ($job.JobStateInfo.State -ne 'Running') {
                        return $false
                    }
                    
                    foreach ($pack in $job.Package) {
                        if ($pack -is [IO.Packaging.Package]) {
                            return $true
                        }
                    }
                }
            return
        }
        
        # If we are passed a uri
        if ($Uri) {
            # pack it up
            $namedParameters['Url'] = $uri
            $namedParameters.Remove('Uri')
            InvokeOpMethod 'Url' $NamedParameters
            return
        }
        
        #region Open Package in At Syntax
        if ($At) {
            $NamedParameters['At'] = $At
            InvokeOpMethod 'At' $NamedParameters
            return
        }
        #endregion Open Package in At Syntax

        #region Open Package from Repository
        if ($Repository) {
            $NamedParameters['Repository'] = $Repository
            InvokeOpMethod 'Repository' $NamedParameters
            return
        }
        #endregion Open Package from Repository

        #region Open Package from At Uri
        if ($AtUri) {
            $NamedParameters['AtUri'] = $AtUri
            InvokeOpMethod 'AtProtocol' $NamedParameters
            return
        }
        #endregion Open Package From At Uri

        if ($Module) {
            if ($module -isnot [Management.Automation.PSModuleInfo]) {
                $loadedModules = @(Get-Module)
                foreach ($moduleInfo in $loadedModules) {
                    if ($moduleInfo.Name -eq $module) {
                        $module = $moduleInfo
                        break
                    }
                }
            }
            if ($module -is [Management.Automation.PSModuleInfo] -and 
                $module.Path) {                
                $namedParameters.Remove('Module')
                $namedParameters.FilePath = $module.Path | Split-Path
                & $myCommandName @namedParameters
                return
            }            
        }

        if ($Dictionary) {
            $namedParameters['DictionaryList'] = $_
            InvokeOpMethod 'Dictionary' $NamedParameters            
            return
        }

        #region Open Package from Nuget
        if ($Nuget) {
            # Try to call our GetNuget method, and pass the `$Nuget`
            InvokeOpMethod 'Nuget' $PSBoundParameters
            return
        }
        #endregion Open Package from Nuget

        #region Open Package from Node
        if ($NodePackage) {
            # Try to call our node method
            InvokeOpMethod 'Node' $PSBoundParameters
            return
        }
        #endregion Open Package from Node

        #region Open Package from Python
        if ($PythonPackage) {
            # Try to call our Python method
            InvokeOpMethod 'Python' $PSBoundParameters
            return
        }
        #endregion Open Package from Python

        if ($filePath) {
            $namedParameters.Remove('FilePath')
            # Try to resolve the file path
            $resolvedPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($FilePath)
            # If we could not resolve the path, exit
            if (-not $resolvedPath ) { return }

            # Get each file beneath the path
            foreach ($resolved in $resolvedPath) {
                # (watch our for escaped characters and hidden files)
                $resolvedItem = Get-Item -LiteralPath ($resolved -replace '`') -Force:$IncludeHidden
                
                # If we could not resolve the item, continue
                if (-not $resolvedItem) { continue }

                # If the item is a file
                if ($resolvedItem -is [IO.FileInfo]) {
                    # make packages from the file
                    $resolvedItem | packFile
                }
                # If the item is a directory
                elseif ($resolvedItem -is [IO.DirectoryInfo]) {
                    # We want a package from a directory
                    # Push into that location, for it will make operations easier
                    Push-Location -LiteralPath $resolvedItem.FullName
                    # Get all files beneath this point
                    $namedParameters['Directory'] = $resolvedItem.FullName
                    InvokeOpMethod 'Directory' $NamedParameters
                    Pop-Location
                    # make packages from the directory
                }
            }

            return
        }

        getCurrentPack
    }
}