Types/OpenPackage.Source/Url.ps1
|
<# .SYNOPSIS Packages urls. .DESCRIPTION Gets one or more urls and stores them in Open Packages #> param( # An array of urls [Alias('Uri')] [uri[]] $url, # A list of file wildcards to include. [Parameter(ValueFromPipelineByPropertyName)] [SupportsWildcards()] [string[]] $Include, # A list of file wildcards to exclude. [Parameter(ValueFromPipelineByPropertyName)] [SupportsWildcards()] [string[]] $Exclude, # The base path within the package. # Content should be added beneath this base path. [string] $BasePath = '/', # 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', # 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. [Alias('IncludeGitFile','IncludeGitFiles','IncludeGitDirectory')] [switch] $IncludeGit, # If set, will include any content found in `/node_modules`. [Alias('IncludeNodeModules')] [switch] $IncludeNodeModule, # The personal data server. This is used in At Protocol requests. [string] $PDS, # Any additional headers to pass into a web request. [Alias('Header')] [Collections.IDictionary] $Headers = [Ordered]@{}, # The current package [IO.Packaging.Package] $Package ) $namedParameters = [Ordered]@{} foreach ($key in $MyInvocation.MyCommand.Parameters.Keys) { $var = $ExecutionContext.SessionState.PSVariable.Get($key) if (-not [String]::IsNullOrEmpty($var.Value)) { $namedParameters[$key] = $var.value } } $namedParameters.Remove('url') $namedParameters.Remove('inputObject') foreach ($uri in $url) { # If the uri is an at:// uri if ($uri.Scheme -eq 'at') { # call ourself Get-OpenPackage -AtUri $uri @namedParameters continue } # If the URI is a git uri, or is a well-known git domain, if ( $uri.Scheme -eq 'git' -or $uri.DnsSafeHost -match '^git(?>hub|lab)\.com$' -or $uri.DnsSafeHost -match 'tangled\.(org|sh)$' -or $uri.DnsSafeHost -match 'codeberg\.org$' -and -not ($uri.DnsSafeHost -match '^(raw|api)') -and -not ($uri.Segments -match '^releases/?$') ) { # pack a repository Get-OpenPackage -Repository $uri @namedParameters continue } # If the URI is a nuget.org or powershellgallery.com link. if ($uri.DnsSafeHost -in 'nuget.org','www.nuget.org' -or $uri.DnsSafeHost -in 'powershellgallery.com','www.powershellgallery.com' -or $uri.DnsSafeHost -in 'community.chocolatey.org') { # If so, call ourselves with the Nuget uri Get-OpenPackage -NuGet $uri @namedParameters continue } # If the URI is a PyPi.org project link if ($uri.DnsSafeHost -in 'pypi.org', 'www.pypi.org' -and $uri.Segments[1] -eq '/project') { # call ourselves with -PythonPackage Get-OpenPackage -PythonPackage $uri @namedParameters continue } # If the URI is a Node Package link if ($uri.DnsSafeHost -in 'npmjs.com', 'www.npmjs.com', 'npmx.dev', 'www.npmx.dev' -and $uri.Segments[1] -eq '/package' ) { # call ourselves with -NodePackage Get-OpenPackage -NodePackage $uri @namedParameters continue } # If the uri is relative if ( (-not $uri.IsAbsoluteUri) ) { $slashKey = "$uri" -replace "^\.?/?", '/' # try to find it in the package if we have one if ($Package -and $Package -is [IO.Packaging.Package] -and $Package.PartExists($slashKey)) { $Package.GetContent($slashKey) } continue } # At this point lets just poke at the uri and make a package. try { if ($headers) { $WebResponse = Invoke-WebRequest -Uri $Uri -Headers $Headers } else { $WebResponse = Invoke-WebRequest -Uri $Uri } } catch { Write-Verbose "$uri - $_" continue } # If the web response is a byte array if ($WebResponse.Content -is [byte[]] -and # and starts with the magic pair of bytes indicate it might be a zip ($WebResponse.Content[0] -eq 80 -and $WebResponse.Content[1] -eq 75) ) { # Create a stream from the response $memoryStream = [IO.MemoryStream]::new($WebResponse.Content) # and open the package $currentPackage = [IO.Packaging.Package]::Open($memoryStream, 'OpenOrCreate', 'ReadWrite') # If that did not work, it will error, if (-not $currentPackage) { continue # and we should continue. } # Attach the memory stream to the package $currentPackage | Add-Member NoteProperty MemoryStream $memoryStream -Force # and emit the package. $currentPackage } else { # Ok, the response was not a package. # Let's turn it into a new package, or put it in the current package. if (-not $package) { $memoryStream = [IO.MemoryStream]::new() $package = [IO.Packaging.Package]::Open($memoryStream, 'OpenOrCreate','ReadWrite') $package.pstypenames.insert(0, 'OP') $package.pstypenames.insert(0, 'OpenPackage') Add-Member NoteProperty MemoryStream $memoryStream -Force -InputObject $package } # Let's use the domain as the identifier if (-not $package.PackageProperties.Identifier) { $package.PackageProperties.Identifier = $uri.DnsSafeHost } # Pick out the content type from the response $responseType, $responseTypeOptions = $WebResponse.Headers['content-type'] -split ';' # And get the major and minor type $majorType, $minorType = $responseType -split '/', 2 $minorType = $minorType -replace '^.+?\+' # replace anything in the minor type before a plus # (this is effectively the extension) $localPath = # If we ask for a root uri if ($uri.LocalPath -match '/$') { # make it an index of the replied content type $uri.LocalPath + 'index.' + $minorType } else { # otherwise, use the path they provided. $uri.LocalPath } # Now that we know where we're putting it, let's create a part $newPart = $package.CreatePart($localPath, $WebResponse.Headers['content-type'], $CompressionOption) # and get the content stream $newStream = $newPart.GetStream() # If the response was a byte array if ($WebResponse.Content -is [byte[]]) { # write that to the stream $newStream.Write($WebResponse.Content, 0, $WebResponse.Content.Length) } else { # otherwise, turn the stringified content into bytes $buffer = $OutputEncoding.GetBytes("$($WebResponse.Content)") # and write that $newStream.Write($buffer, 0, $buffer.Length) } $newStream.Close() $newStream.Dispose() } } # If there is a current package if ($package) { $package # output it at the end # (this avoids returning the same package multiple times) } |