PSPublishModule.psm1

function Compress-Archive {
    [CmdletBinding(DefaultParameterSetName = "Path",
        SupportsShouldProcess = $true,
        HelpUri = "https://go.microsoft.com/fwlink/?LinkID=393252")]
    param
    ([parameter (mandatory = $true, Position = 0, ParameterSetName = "Path", ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [parameter (mandatory = $true, Position = 0, ParameterSetName = "PathWithForce", ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [parameter (mandatory = $true, Position = 0, ParameterSetName = "PathWithUpdate", ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [string[]] $Path,
        [parameter (mandatory = $true, ParameterSetName = "LiteralPath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $true)]
        [parameter (mandatory = $true, ParameterSetName = "LiteralPathWithForce", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $true)]
        [parameter (mandatory = $true, ParameterSetName = "LiteralPathWithUpdate", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias("PSPath")]
        [string[]] $LiteralPath,
        [parameter (mandatory = $true,
            Position = 1,
            ValueFromPipeline = $false,
            ValueFromPipelineByPropertyName = $false)]
        [ValidateNotNullOrEmpty()]
        [string] $DestinationPath,
        [parameter (mandatory = $false,
            ValueFromPipeline = $false,
            ValueFromPipelineByPropertyName = $false)]
        [ValidateSet("Optimal", "NoCompression", "Fastest")]
        [string]
        $CompressionLevel = "Optimal",
        [parameter(mandatory = $true, ParameterSetName = "PathWithUpdate", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)]
        [parameter(mandatory = $true, ParameterSetName = "LiteralPathWithUpdate", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)]
        [switch]
        $Update = $false,
        [parameter(mandatory = $true, ParameterSetName = "PathWithForce", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)]
        [parameter(mandatory = $true, ParameterSetName = "LiteralPathWithForce", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)]
        [switch]
        $Force = $false)
    BEGIN {
        $inputPaths = @()
        $destinationParentDir = [system.IO.Path]::GetDirectoryName($DestinationPath)
        if ($null -eq $destinationParentDir) {
            $errorMessage = ($LocalizedData.InvalidDestinationPath -f $DestinationPath)
            ThrowTerminatingErrorHelper "InvalidArchiveFilePath" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $DestinationPath
        }
        if ($destinationParentDir -eq [string]::Empty)
        { $destinationParentDir = '.' }
        $achiveFileName = [system.IO.Path]::GetFileName($DestinationPath)
        $destinationParentDir = GetResolvedPathHelper $destinationParentDir $false $PSCmdlet
        if ($destinationParentDir.Count -gt 1) {
            $errorMessage = ($LocalizedData.InvalidArchiveFilePathError -f $DestinationPath, "DestinationPath", "DestinationPath")
            ThrowTerminatingErrorHelper "InvalidArchiveFilePath" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $DestinationPath
        }
        IsValidFileSystemPath $destinationParentDir | Out-Null
        $DestinationPath = Join-Path -Path $destinationParentDir -ChildPath $achiveFileName
        $extension = [system.IO.Path]::GetExtension($DestinationPath)
        If ($extension -eq [string]::Empty) {
            $DestinationPathWithOutExtension = $DestinationPath
            $DestinationPath = $DestinationPathWithOutExtension + $zipFileExtension
            $appendArchiveFileExtensionMessage = ($LocalizedData.AppendArchiveFileExtensionMessage -f $DestinationPathWithOutExtension, $DestinationPath)
            Write-Verbose $appendArchiveFileExtensionMessage
        } else {
            if ($extension -ne $zipFileExtension) {
                $errorMessage = ($LocalizedData.InvalidZipFileExtensionError -f $extension, $zipFileExtension)
                ThrowTerminatingErrorHelper "NotSupportedArchiveFileExtension" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $extension
            }
        }
        $archiveFileExist = Test-Path -LiteralPath $DestinationPath -PathType Leaf
        if ($archiveFileExist -and ($Update -eq $false -and $Force -eq $false)) {
            $errorMessage = ($LocalizedData.ZipFileExistError -f $DestinationPath)
            ThrowTerminatingErrorHelper "ArchiveFileExists" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $DestinationPath
        }
        if ($archiveFileExist -and $Update -eq $true) {
            $item = Get-Item -Path $DestinationPath
            if ($item.Attributes.ToString().Contains("ReadOnly")) {
                $errorMessage = ($LocalizedData.ArchiveFileIsReadOnly -f $DestinationPath)
                ThrowTerminatingErrorHelper "ArchiveFileIsReadOnly" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidOperation) $DestinationPath
            }
        }
        $isWhatIf = $psboundparameters.ContainsKey("WhatIf")
        if (!$isWhatIf) {
            $preparingToCompressVerboseMessage = ($LocalizedData.PreparingToCompressVerboseMessage)
            Write-Verbose $preparingToCompressVerboseMessage
            $progressBarStatus = ($LocalizedData.CompressProgressBarText -f $DestinationPath)
            ProgressBarHelper "Compress-Archive" $progressBarStatus 0 100 100 1
        }
    }
    PROCESS {
        if ($PsCmdlet.ParameterSetName -eq "Path" -or
            $PsCmdlet.ParameterSetName -eq "PathWithForce" -or
            $PsCmdlet.ParameterSetName -eq "PathWithUpdate")
        { $inputPaths += $Path }
        if ($PsCmdlet.ParameterSetName -eq "LiteralPath" -or
            $PsCmdlet.ParameterSetName -eq "LiteralPathWithForce" -or
            $PsCmdlet.ParameterSetName -eq "LiteralPathWithUpdate")
        { $inputPaths += $LiteralPath }
    }
    END {
        if (($PsCmdlet.ParameterSetName -eq "PathWithForce" -or
                $PsCmdlet.ParameterSetName -eq "LiteralPathWithForce") -and $archiveFileExist)
        { Remove-Item -Path $DestinationPath -Force -ErrorAction Stop }
        $isLiteralPathUsed = $false
        if ($PsCmdlet.ParameterSetName -eq "LiteralPath" -or
            $PsCmdlet.ParameterSetName -eq "LiteralPathWithForce" -or
            $PsCmdlet.ParameterSetName -eq "LiteralPathWithUpdate")
        { $isLiteralPathUsed = $true }
        ValidateDuplicateFileSystemPath $PsCmdlet.ParameterSetName $inputPaths
        $resolvedPaths = GetResolvedPathHelper $inputPaths $isLiteralPathUsed $PSCmdlet
        IsValidFileSystemPath $resolvedPaths | Out-Null
        $sourcePath = $resolvedPaths
        $sourcePathInCsvFormat = CSVHelper $sourcePath
        if ($pscmdlet.ShouldProcess($sourcePathInCsvFormat)) {
            try {
                $isArchiveFileProcessingComplete = $false
                $numberOfItemsArchived = CompressArchiveHelper $sourcePath $DestinationPath $CompressionLevel $Update
                $isArchiveFileProcessingComplete = $true
            } finally {
                if (($isArchiveFileProcessingComplete -eq $false) -or
                    ($numberOfItemsArchived -eq 0)) {
                    $DeleteArchiveFileMessage = ($LocalizedData.DeleteArchiveFile -f $DestinationPath)
                    Write-Verbose $DeleteArchiveFileMessage
                    if (Test-Path $DestinationPath) { Remove-Item -LiteralPath $DestinationPath -Force -Recurse -ErrorAction SilentlyContinue }
                }
            }
        }
    }
}
function Import-PowerShellDataFile {
    [CmdletBinding(DefaultParameterSetName = "ByPath", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=623621")]
    [OutputType("System.Collections.Hashtable")]
    param([Parameter(ParameterSetName = "ByPath", Position = 0)]
        [String[]] $Path,
        [Parameter(ParameterSetName = "ByLiteralPath", ValueFromPipelineByPropertyName = $true)]
        [Alias("PSPath")]
        [String[]] $LiteralPath)
    begin {
        function ThrowInvalidDataFile {
            param($resolvedPath, $extraError)
            $errorId = "CouldNotParseAsPowerShellDataFile$extraError"
            $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidData
            $errorMessage = [Microsoft.PowerShell.Commands.UtilityResources]::CouldNotParseAsPowerShellDataFile -f $resolvedPath
            $exception = [System.InvalidOperationException]::New($errorMessage)
            $errorRecord = [System.Management.Automation.ErrorRecord]::New($exception, $errorId, $errorCategory, $null)
            $PSCmdlet.WriteError($errorRecord)
        }
    }
    process {
        foreach ($resolvedPath in (Resolve-Path @PSBoundParameters)) {
            $parseErrors = $null
            $ast = [System.Management.Automation.Language.Parser]::ParseFile(($resolvedPath.ProviderPath), [ref] $null, [ref] $parseErrors)
            if ($parseErrors.Length -gt 0)
            { ThrowInvalidDataFile $resolvedPath }
            else {
                $data = $ast.Find( { $args[0] -is [System.Management.Automation.Language.HashtableAst] }, $false)
                if ($data)
                { $data.SafeGetValue() }
                else
                { ThrowInvalidDataFile $resolvedPath "NoHashtableRoot" }
            }
        }
    }
}
function New-MarkdownHelp {
    [CmdletBinding()]
    [OutputType([System.IO.FileInfo[]])]
    param([Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            ParameterSetName = "FromModule")]
        [string[]]$Module,
        [Parameter(Mandatory = $true,
            ParameterSetName = "FromCommand")]
        [string[]]$Command,
        [Parameter(Mandatory = $true,
            ParameterSetName = "FromMaml")]
        [string[]]$MamlFile,
        [Parameter(ParameterSetName = "FromModule")]
        [Parameter(ParameterSetName = "FromCommand")]
        [System.Management.Automation.Runspaces.PSSession]$Session,
        [Parameter(ParameterSetName = "FromMaml")]
        [switch]$ConvertNotesToList,
        [Parameter(ParameterSetName = "FromMaml")]
        [switch]$ConvertDoubleDashLists,
        [switch]$Force,
        [switch]$AlphabeticParamsOrder,
        [hashtable]$Metadata,
        [Parameter(ParameterSetName = "FromCommand")]
        [string]$OnlineVersionUrl = '',
        [Parameter(Mandatory = $true)]
        [string]$OutputFolder,
        [switch]$NoMetadata,
        [switch]$UseFullTypeName,
        [System.Text.Encoding]$Encoding = $script:UTF8_NO_BOM,
        [Parameter(ParameterSetName = "FromModule")]
        [Parameter(ParameterSetName = "FromMaml")]
        [switch]$WithModulePage,
        [Parameter(ParameterSetName = "FromModule")]
        [Parameter(ParameterSetName = "FromMaml")]
        [string]$ModulePagePath,
        [Parameter(ParameterSetName = "FromModule")]
        [Parameter(ParameterSetName = "FromMaml")]
        [string]
        $Locale = "en-US",
        [Parameter(ParameterSetName = "FromModule")]
        [Parameter(ParameterSetName = "FromMaml")]
        [string]
        $HelpVersion = $LocalizedData.HelpVersion,
        [Parameter(ParameterSetName = "FromModule")]
        [Parameter(ParameterSetName = "FromMaml")]
        [string]
        $FwLink = $LocalizedData.FwLink,
        [Parameter(ParameterSetName = "FromMaml")]
        [string]
        $ModuleName = "MamlModule",
        [Parameter(ParameterSetName = "FromMaml")]
        [string]
        $ModuleGuid = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
        [switch]
        $ExcludeDontShow)
    begin {
        validateWorkingProvider
        New-Item -Type Directory $OutputFolder -ErrorAction SilentlyContinue > $null
    }
    process {
        function updateMamlObject {
            param([Parameter(Mandatory = $true)]
                [Markdown.MAML.Model.MAML.MamlCommand]$MamlCommandObject)
            if ($MamlCommandObject.Examples.Count -eq 0) {
                $MamlExampleObject = New-Object -TypeName Markdown.MAML.Model.MAML.MamlExample
                $MamlExampleObject.Title = $LocalizedData.ExampleTitle
                $MamlExampleObject.Code = @(New-Object -TypeName Markdown.MAML.Model.MAML.MamlCodeBlock ($LocalizedData.ExampleCode, 'powershell'))
                $MamlExampleObject.Remarks = $LocalizedData.ExampleRemark
                $MamlCommandObject.Examples.Add($MamlExampleObject)
            }
            if ($AlphabeticParamsOrder)
            { SortParamsAlphabetically $MamlCommandObject }
        }
        function processMamlObjectToFile {
            param([Parameter(ValueFromPipeline = $true)]
                [ValidateNotNullOrEmpty()]
                [Markdown.MAML.Model.MAML.MamlCommand]$mamlObject)
            process {
                updateMamlObject $mamlObject
                if (-not $OnlineVersionUrl) {
                    $onlineLink = $mamlObject.Links | Select-Object -First 1
                    if ($onlineLink) {
                        $online = $onlineLink.LinkUri
                        if ($onlineLink.LinkName -eq $script:MAML_ONLINE_LINK_DEFAULT_MONIKER -or $onlineLink.LinkName -eq $onlineLink.LinkUri)
                        { $mamlObject.Links.Remove($onlineLink) > $null }
                    }
                } else
                { $online = $OnlineVersionUrl }
                $commandName = $mamlObject.Name
                if ($NoMetadata)
                { $newMetadata = $null }
                else {
                    if ($MamlFile)
                    { $helpFileName = Split-Path -Leaf $MamlFile }
                    else {
                        if ($mamlObject.Name.EndsWith(".ps1"))
                        { $getCommandName = Resolve-Path $Command }
                        else
                        { $getCommandName = $commandName }
                        $a = @{Name = $getCommandName }
                        if ($module) { $a['Module'] = $module }
                        $helpFileName = GetHelpFileName (Get-Command @a)
                    }
                    Write-Verbose "Maml things module is: $($mamlObject.ModuleName)"
                    $newMetadata = ($Metadata + @{$script:EXTERNAL_HELP_FILE_YAML_HEADER = $helpFileName
                            $script:ONLINE_VERSION_YAML_HEADER                           = $online
                            $script:MODULE_PAGE_MODULE_NAME                              = $mamlObject.ModuleName
                        })
                }
                $md = ConvertMamlModelToMarkdown -mamlCommand $mamlObject -metadata $newMetadata -NoMetadata:$NoMetadata
                MySetContent -path (Join-Path $OutputFolder "$commandName.md") -value $md -Encoding $Encoding -Force:$Force
            }
        }
        if ($NoMetadata -and $Metadata)
        { throw $LocalizedData.NoMetadataAndMetadata }
        if ($PSCmdlet.ParameterSetName -eq 'FromCommand') {
            $command | ForEach-Object { if (-not (Get-Command $_ -ErrorAction SilentlyContinue))
                { throw $LocalizedData.CommandNotFound -f $_ }
                GetMamlObject -Session $Session -Cmdlet $_ -UseFullTypeName:$UseFullTypeName -ExcludeDontShow:$ExcludeDontShow.IsPresent | processMamlObjectToFile }
        } else {
            if ($module)
            { $iterator = $module }
            else
            { $iterator = $MamlFile }
            $iterator | ForEach-Object { if ($PSCmdlet.ParameterSetName -eq 'FromModule') {
                    if (-not (GetCommands -AsNames -module $_))
                    { throw $LocalizedData.ModuleNotFound -f $_ }
                    GetMamlObject -Session $Session -Module $_ -UseFullTypeName:$UseFullTypeName -ExcludeDontShow:$ExcludeDontShow.IsPresent | processMamlObjectToFile
                    $ModuleName = $_
                    $ModuleGuid = (Get-Module $ModuleName).Guid
                    $CmdletNames = GetCommands -AsNames -Module $ModuleName
                } else {
                    if (-not (Test-Path $_))
                    { throw $LocalizedData.FileNotFound -f $_ }
                    GetMamlObject -MamlFile $_ -ConvertNotesToList:$ConvertNotesToList -ConvertDoubleDashLists:$ConvertDoubleDashLists -ExcludeDontShow:$ExcludeDontShow.IsPresent | processMamlObjectToFile
                    $CmdletNames += GetMamlObject -MamlFile $_ -ExcludeDontShow:$ExcludeDontShow.IsPresent | ForEach-Object { $_.Name }
                }
                if ($WithModulePage) {
                    if (-not $ModuleGuid)
                    { $ModuleGuid = "00000000-0000-0000-0000-000000000000" }
                    if ($ModuleGuid.Count -gt 1)
                    { Write-Warning -Message $LocalizedData.MoreThanOneGuid }
                    NewModuleLandingPage -Path $OutputFolder -ModulePagePath $ModulePagePath -ModuleName $ModuleName -ModuleGuid $ModuleGuid -CmdletNames $CmdletNames -Locale $Locale -Version $HelpVersion -FwLink $FwLink -Encoding $Encoding -Force:$Force
                } }
        }
    }
}
function Publish-Module {
    <#
    .ExternalHelp PSModule-help.xml
    #>

    [CmdletBinding(SupportsShouldProcess = $true,
        PositionalBinding = $false,
        HelpUri = 'https://go.microsoft.com/fwlink/?LinkID=398575',
        DefaultParameterSetName = "ModuleNameParameterSet")]
    Param
    ([Parameter(Mandatory = $true,
            ParameterSetName = "ModuleNameParameterSet",
            ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [string]
        $Name,
        [Parameter(Mandatory = $true,
            ParameterSetName = "ModulePathParameterSet",
            ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [string]
        $Path,
        [Parameter(ParameterSetName = "ModuleNameParameterSet")]
        [ValidateNotNullOrEmpty()]
        [string]
        $RequiredVersion,
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]
        $NuGetApiKey,
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]
        $Repository = $Script:PSGalleryModuleSource,
        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [PSCredential]
        $Credential,
        [Parameter()]
        [ValidateSet("2.0")]
        [Version]
        $FormatVersion,
        [Parameter()]
        [string[]]
        $ReleaseNotes,
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string[]]
        $Tags,
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [Uri]
        $LicenseUri,
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [Uri]
        $IconUri,
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [Uri]
        $ProjectUri,
        [Parameter(ParameterSetName = "ModuleNameParameterSet")]
        [ValidateNotNullOrEmpty()]
        [string[]]
        $Exclude,
        [Parameter()]
        [switch]
        $Force,
        [Parameter(ParameterSetName = "ModuleNameParameterSet")]
        [switch]
        $AllowPrerelease,
        [Parameter()]
        [switch]
        $SkipAutomaticTags)
    Begin {
        if ($LicenseUri -and -not (Test-WebUri -uri $LicenseUri)) {
            $message = $LocalizedData.InvalidWebUri -f ($LicenseUri, "LicenseUri")
            ThrowError -ExceptionName "System.ArgumentException" -ExceptionMessage $message -ErrorId "InvalidWebUri" -CallerPSCmdlet $PSCmdlet -ErrorCategory InvalidArgument -ExceptionObject $LicenseUri
        }
        if ($IconUri -and -not (Test-WebUri -uri $IconUri)) {
            $message = $LocalizedData.InvalidWebUri -f ($IconUri, "IconUri")
            ThrowError -ExceptionName "System.ArgumentException" -ExceptionMessage $message -ErrorId "InvalidWebUri" -CallerPSCmdlet $PSCmdlet -ErrorCategory InvalidArgument -ExceptionObject $IconUri
        }
        if ($ProjectUri -and -not (Test-WebUri -uri $ProjectUri)) {
            $message = $LocalizedData.InvalidWebUri -f ($ProjectUri, "ProjectUri")
            ThrowError -ExceptionName "System.ArgumentException" -ExceptionMessage $message -ErrorId "InvalidWebUri" -CallerPSCmdlet $PSCmdlet -ErrorCategory InvalidArgument -ExceptionObject $ProjectUri
        }
        Install-NuGetClientBinaries -CallerPSCmdlet $PSCmdlet -BootstrapNuGetExe -Force:$Force
    }
    Process {
        if ($Repository -eq $Script:PSGalleryModuleSource) {
            $moduleSource = Get-PSRepository -Name $Repository -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
            if (-not $moduleSource) {
                $message = $LocalizedData.PSGalleryNotFound -f ($Repository)
                ThrowError -ExceptionName "System.ArgumentException" -ExceptionMessage $message -ErrorId 'PSGalleryNotFound' -CallerPSCmdlet $PSCmdlet -ErrorCategory InvalidArgument -ExceptionObject $Repository
                return
            }
        } else {
            $ev = $null
            $moduleSource = Get-PSRepository -Name $Repository -ErrorVariable ev
            if ($ev) { return }
        }
        $DestinationLocation = $moduleSource.PublishLocation
        if (-not $DestinationLocation -or
            (-not (Microsoft.PowerShell.Management\Test-Path $DestinationLocation) -and
                -not (Test-WebUri -uri $DestinationLocation))) {
            $message = $LocalizedData.PSGalleryPublishLocationIsMissing -f ($Repository, $Repository)
            ThrowError -ExceptionName "System.ArgumentException" -ExceptionMessage $message -ErrorId "PSGalleryPublishLocationIsMissing" -CallerPSCmdlet $PSCmdlet -ErrorCategory InvalidArgument -ExceptionObject $Repository
        }
        $message = $LocalizedData.PublishLocation -f ($DestinationLocation)
        Write-Verbose -Message $message
        if (-not $NuGetApiKey.Trim()) {
            if (Microsoft.PowerShell.Management\Test-Path -Path $DestinationLocation) { $NuGetApiKey = "$(Get-Random)" }
            else {
                $message = $LocalizedData.NuGetApiKeyIsRequiredForNuGetBasedGalleryService -f ($Repository, $DestinationLocation)
                ThrowError -ExceptionName "System.ArgumentException" -ExceptionMessage $message -ErrorId "NuGetApiKeyIsRequiredForNuGetBasedGalleryService" -CallerPSCmdlet $PSCmdlet -ErrorCategory InvalidArgument
            }
        }
        $providerName = Get-ProviderName -PSCustomObject $moduleSource
        if ($providerName -ne $script:NuGetProviderName) {
            $message = $LocalizedData.PublishModuleSupportsOnlyNuGetBasedPublishLocations -f ($moduleSource.PublishLocation, $Repository, $Repository)
            ThrowError -ExceptionName "System.ArgumentException" -ExceptionMessage $message -ErrorId "PublishModuleSupportsOnlyNuGetBasedPublishLocations" -CallerPSCmdlet $PSCmdlet -ErrorCategory InvalidArgument -ExceptionObject $Repository
        }
        $moduleName = $null
        if ($Name) {
            if ($RequiredVersion) {
                $ValidationResult = Validate-VersionParameters -CallerPSCmdlet $PSCmdlet -Name $Name -RequiredVersion $RequiredVersion -AllowPrerelease:$AllowPrerelease
                if (-not $ValidationResult) { return }
                $reqResult = ValidateAndGet-VersionPrereleaseStrings -Version $RequiredVersion -CallerPSCmdlet $PSCmdlet
                if (-not $reqResult) { return }
                $reqVersion = $reqResult["Version"]
                $reqPrerelease = $reqResult["Prerelease"]
            } else {
                $reqVersion = $null
                $reqPrerelease = $null
            }
            $module = Microsoft.PowerShell.Core\Get-Module -ListAvailable -Name $Name -Verbose:$false |
                Microsoft.PowerShell.Core\Where-Object { $modInfoPrerelease = $null
                    if ($_.PrivateData -and
                        $_.PrivateData.GetType().ToString() -eq "System.Collections.Hashtable" -and
                        $_.PrivateData["PSData"] -and
                        $_.PrivateData.PSData.GetType().ToString() -eq "System.Collections.Hashtable" -and
                        $_.PrivateData.PSData["Prerelease"]) { $modInfoPrerelease = $_.PrivateData.PSData.Prerelease }
                    (-not $RequiredVersion) -or (($reqVersion -eq $_.Version) -and ($reqPrerelease -match $modInfoPrerelease)) }
            if (-not $module) {
                if ($RequiredVersion) { $message = $LocalizedData.ModuleWithRequiredVersionNotAvailableLocally -f ($Name, $RequiredVersion) }
                else { $message = $LocalizedData.ModuleNotAvailableLocally -f ($Name) }
                ThrowError -ExceptionName "System.ArgumentException" -ExceptionMessage $message -ErrorId "ModuleNotAvailableLocallyToPublish" -CallerPSCmdlet $PSCmdlet -ErrorCategory InvalidArgument -ExceptionObject $Name
            } elseif ($module.GetType().ToString() -ne "System.Management.Automation.PSModuleInfo") {
                $message = $LocalizedData.AmbiguousModuleName -f ($Name)
                ThrowError -ExceptionName "System.ArgumentException" -ExceptionMessage $message -ErrorId "AmbiguousModuleNameToPublish" -CallerPSCmdlet $PSCmdlet -ErrorCategory InvalidArgument -ExceptionObject $Name
            }
            $moduleName = $module.Name
            $Path = $module.ModuleBase
        } else {
            $resolvedPath = Resolve-PathHelper -Path $Path -CallerPSCmdlet $PSCmdlet | Microsoft.PowerShell.Utility\Select-Object -First 1 -ErrorAction Ignore
            if (-not $resolvedPath -or
                -not (Microsoft.PowerShell.Management\Test-Path -Path $resolvedPath -PathType Container)) {
                ThrowError -ExceptionName "System.ArgumentException" -ExceptionMessage ($LocalizedData.PathIsNotADirectory -f ($Path)) -ErrorId "PathIsNotADirectory" -CallerPSCmdlet $PSCmdlet -ErrorCategory InvalidArgument -ExceptionObject $Path
                return
            }
            $moduleName = Microsoft.PowerShell.Management\Split-Path -Path $resolvedPath -Leaf
            $modulePathWithVersion = $false
            [Version]$ModuleVersion = $null
            if ([System.Version]::TryParse($moduleName, ([ref]$ModuleVersion))) {
                $moduleName = Microsoft.PowerShell.Management\Split-Path -Path (Microsoft.PowerShell.Management\Split-Path $resolvedPath -Parent) -Leaf
                $modulePathWithVersion = $true
            }
            $manifestPath = Join-PathUtility -Path $resolvedPath -ChildPath "$moduleName.psd1" -PathType File
            $module = $null
            if (Microsoft.PowerShell.Management\Test-Path -Path $manifestPath -PathType Leaf) {
                $ev = $null
                $module = Microsoft.PowerShell.Core\Test-ModuleManifest -Path $manifestPath -ErrorVariable ev -Verbose:$VerbosePreference
                if ($ev) { return }
            } elseif (-not $modulePathWithVersion -and ($PSVersionTable.PSVersion -ge '5.0.0')) { $module = Microsoft.PowerShell.Core\Get-Module -Name $resolvedPath -ListAvailable -ErrorAction SilentlyContinue -Verbose:$false }
            if (-not $module) {
                $message = $LocalizedData.InvalidModulePathToPublish -f ($Path)
                ThrowError -ExceptionName "System.ArgumentException" -ExceptionMessage $message -ErrorId 'InvalidModulePathToPublish' -CallerPSCmdlet $PSCmdlet -ErrorCategory InvalidArgument -ExceptionObject $Path
            } elseif ($module.GetType().ToString() -ne "System.Management.Automation.PSModuleInfo") {
                $message = $LocalizedData.AmbiguousModulePath -f ($Path)
                ThrowError -ExceptionName "System.ArgumentException" -ExceptionMessage $message -ErrorId 'AmbiguousModulePathToPublish' -CallerPSCmdlet $PSCmdlet -ErrorCategory InvalidArgument -ExceptionObject $Path
            }
            if ($module -and (-not $module.Path.EndsWith('.psd1', [System.StringComparison]::OrdinalIgnoreCase))) {
                $message = $LocalizedData.InvalidModuleToPublish -f ($module.Name)
                ThrowError -ExceptionName "System.InvalidOperationException" -ExceptionMessage $message -ErrorId "InvalidModuleToPublish" -CallerPSCmdlet $PSCmdlet -ErrorCategory InvalidOperation -ExceptionObject $module.Name
            }
            $moduleName = $module.Name
            $Path = $module.ModuleBase
        }
        $message = $LocalizedData.PublishModuleLocation -f ($moduleName, $Path)
        Write-Verbose -Message $message
        if ($Tags) {
            $message = $LocalizedData.TagsShouldBeIncludedInManifestFile -f ($moduleName, $Path)
            Write-Warning $message
        }
        if ($ReleaseNotes) {
            $message = $LocalizedData.ReleaseNotesShouldBeIncludedInManifestFile -f ($moduleName, $Path)
            Write-Warning $message
        }
        if ($LicenseUri) {
            $message = $LocalizedData.LicenseUriShouldBeIncludedInManifestFile -f ($moduleName, $Path)
            Write-Warning $message
        }
        if ($IconUri) {
            $message = $LocalizedData.IconUriShouldBeIncludedInManifestFile -f ($moduleName, $Path)
            Write-Warning $message
        }
        if ($ProjectUri) {
            $message = $LocalizedData.ProjectUriShouldBeIncludedInManifestFile -f ($moduleName, $Path)
            Write-Warning $message
        }
        $tempModulePath = Microsoft.PowerShell.Management\Join-Path -Path $script:TempPath -ChildPath "$(Microsoft.PowerShell.Utility\Get-Random)\$moduleName"
        if ($FormatVersion -eq "1.0") { $tempModulePathForFormatVersion = Microsoft.PowerShell.Management\Join-Path $tempModulePath "Content\Deployment\$script:ModuleReferences\$moduleName" }
        else { $tempModulePathForFormatVersion = $tempModulePath }
        $null = Microsoft.PowerShell.Management\New-Item -Path $tempModulePathForFormatVersion -ItemType Directory -Force -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -Confirm:$false -WhatIf:$false
        Microsoft.PowerShell.Management\Get-ChildItem $Path -recurse |
            Microsoft.PowerShell.Management\Copy-Item -Force -Confirm:$false -WhatIf:$false -Destination { if ($_.PSIsContainer) { Join-Path $tempModulePathForFormatVersion $_.Parent.FullName.substring($path.length) }
                else { Join-Path $tempModulePathForFormatVersion $_.FullName.Substring($path.Length) } }
        try {
            $manifestPath = Join-PathUtility -Path $tempModulePathForFormatVersion -ChildPath "$moduleName.psd1" -PathType File
            if (-not (Microsoft.PowerShell.Management\Test-Path $manifestPath)) {
                $message = $LocalizedData.InvalidModuleToPublish -f ($moduleName)
                ThrowError -ExceptionName "System.InvalidOperationException" -ExceptionMessage $message -ErrorId "InvalidModuleToPublish" -CallerPSCmdlet $PSCmdlet -ErrorCategory InvalidOperation -ExceptionObject $moduleName
            }
            $ev = $null
            $moduleInfo = Microsoft.PowerShell.Core\Test-ModuleManifest -Path $manifestPath -ErrorVariable ev -Verbose:$VerbosePreference
            if ($ev) { return }
            if (-not $moduleInfo -or
                -not $moduleInfo.Author -or
                -not $moduleInfo.Description) {
                $message = $LocalizedData.MissingRequiredManifestKeys -f ($moduleName)
                ThrowError -ExceptionName "System.InvalidOperationException" -ExceptionMessage $message -ErrorId "MissingRequiredModuleManifestKeys" -CallerPSCmdlet $PSCmdlet -ErrorCategory InvalidOperation -ExceptionObject $moduleName
            }
            $moduleInfoPrerelease = $null
            if ($moduleInfo.PrivateData -and
                $moduleInfo.PrivateData.GetType().ToString() -eq "System.Collections.Hashtable" -and
                $moduleInfo.PrivateData["PSData"] -and
                $moduleInfo.PrivateData.PSData.GetType().ToString() -eq "System.Collections.Hashtable" -and
                $moduleInfo.PrivateData.PSData["Prerelease"]) { $moduleInfoPrerelease = $moduleInfo.PrivateData.PSData.Prerelease }
            $result = ValidateAndGet-VersionPrereleaseStrings -Version $moduleInfo.Version -Prerelease $moduleInfoPrerelease -CallerPSCmdlet $PSCmdlet
            if (-not $result) { return }
            $moduleInfoVersion = $result["Version"]
            $moduleInfoPrerelease = $result["Prerelease"]
            $moduleInfoFullVersion = $result["FullVersion"]
            $FindParameters = @{Name = $moduleName
                Repository           = $Repository
                Tag                  = 'PSScript'
                AllowPrerelease      = $true
                Verbose              = $VerbosePreference
                ErrorAction          = 'SilentlyContinue'
                WarningAction        = 'SilentlyContinue'
                Debug                = $DebugPreference
            }
            if ($Credential) { $FindParameters[$script:Credential] = $Credential }
            $scriptPSGetItemInfo = Find-Script @FindParameters |
                Microsoft.PowerShell.Core\Where-Object { $_.Name -eq $moduleName } |
                    Microsoft.PowerShell.Utility\Select-Object -Last 1 -ErrorAction Ignore
            if ($scriptPSGetItemInfo) {
                $message = $LocalizedData.SpecifiedNameIsAlearyUsed -f ($moduleName, $Repository, 'Find-Script')
                ThrowError -ExceptionName "System.InvalidOperationException" -ExceptionMessage $message -ErrorId "SpecifiedNameIsAlearyUsed" -CallerPSCmdlet $PSCmdlet -ErrorCategory InvalidOperation -ExceptionObject $moduleName
            }
            $null = $FindParameters.Remove('Tag')
            $currentPSGetItemInfo = Find-Module @FindParameters |
                Microsoft.PowerShell.Core\Where-Object { $_.Name -eq $moduleInfo.Name } |
                    Microsoft.PowerShell.Utility\Select-Object -Last 1 -ErrorAction Ignore
            if ($currentPSGetItemInfo) {
                $result = ValidateAndGet-VersionPrereleaseStrings -Version $currentPSGetItemInfo.Version -CallerPSCmdlet $PSCmdlet
                if (-not $result) { return }
                $currentPSGetItemVersion = $result["Version"]
                $currentPSGetItemPrereleaseString = $result["Prerelease"]
                $currentPSGetItemFullVersion = $result["FullVersion"]
                if ($currentPSGetItemVersion -eq $moduleInfoVersion) {
                    if (-not $currentPSGetItemPrereleaseString -and -not $moduleInfoPrerelease) {
                        $message = $LocalizedData.ModuleVersionIsAlreadyAvailableInTheGallery -f ($moduleInfo.Name, $moduleInfoFullVersion, $currentPSGetItemFullVersion, $currentPSGetItemInfo.RepositorySourceLocation)
                        ThrowError -ExceptionName 'System.InvalidOperationException' -ExceptionMessage $message -ErrorId 'ModuleVersionIsAlreadyAvailableInTheGallery' -CallerPSCmdlet $PSCmdlet -ErrorCategory InvalidOperation
                    } elseif (-not $Force -and (-not $currentPSGetItemPrereleaseString -and $moduleInfoPrerelease)) {
                        $message = $LocalizedData.ModuleVersionShouldBeGreaterThanGalleryVersion -f ($moduleInfo.Name, $moduleInfoFullVersion, $currentPSGetItemFullVersion, $currentPSGetItemInfo.RepositorySourceLocation)
                        ThrowError -ExceptionName "System.InvalidOperationException" -ExceptionMessage $message -ErrorId "ModuleVersionShouldBeGreaterThanGalleryVersion" -CallerPSCmdlet $PSCmdlet -ErrorCategory InvalidOperation
                    } elseif ($currentPSGetItemPrereleaseString -and $moduleInfoPrerelease) {
                        if ($currentPSGetItemPrereleaseString -eq $moduleInfoPrerelease) {
                            $message = $LocalizedData.ModuleVersionIsAlreadyAvailableInTheGallery -f ($moduleInfo.Name, $moduleInfoFullVersion, $currentPSGetItemFullVersion, $currentPSGetItemInfo.RepositorySourceLocation)
                            ThrowError -ExceptionName 'System.InvalidOperationException' -ExceptionMessage $message -ErrorId 'ModuleVersionIsAlreadyAvailableInTheGallery' -CallerPSCmdlet $PSCmdlet -ErrorCategory InvalidOperation
                        } elseif (-not $Force -and ($currentPSGetItemPrereleaseString -gt $moduleInfoPrerelease)) {
                            $message = $LocalizedData.ModuleVersionShouldBeGreaterThanGalleryVersion -f ($moduleInfo.Name, $moduleInfoFullVersion, $currentPSGetItemFullVersion, $currentPSGetItemInfo.RepositorySourceLocation)
                            ThrowError -ExceptionName "System.InvalidOperationException" -ExceptionMessage $message -ErrorId "ModuleVersionShouldBeGreaterThanGalleryVersion" -CallerPSCmdlet $PSCmdlet -ErrorCategory InvalidOperation
                        }
                    }
                } elseif (-not $Force -and (Compare-PrereleaseVersions -FirstItemVersion $moduleInfoVersion -FirstItemPrerelease $moduleInfoPrerelease -SecondItemVersion $currentPSGetItemVersion -SecondItemPrerelease $currentPSGetItemPrereleaseString)) {
                    $message = $LocalizedData.ModuleVersionShouldBeGreaterThanGalleryVersion -f ($moduleInfo.Name, $moduleInfoVersion, $currentPSGetItemFullVersion, $currentPSGetItemInfo.RepositorySourceLocation)
                    ThrowError -ExceptionName "System.InvalidOperationException" -ExceptionMessage $message -ErrorId "ModuleVersionShouldBeGreaterThanGalleryVersion" -CallerPSCmdlet $PSCmdlet -ErrorCategory InvalidOperation
                }
            }
            $shouldProcessMessage = $LocalizedData.PublishModulewhatIfMessage -f ($moduleInfo.Version, $moduleInfo.Name)
            if ($Force -or $PSCmdlet.ShouldProcess($shouldProcessMessage, "Publish-Module")) {
                $PublishPSArtifactUtility_Params = @{PSModuleInfo = $moduleInfo
                    ManifestPath                                  = $manifestPath
                    NugetApiKey                                   = $NuGetApiKey
                    Destination                                   = $DestinationLocation
                    Repository                                    = $Repository
                    NugetPackageRoot                              = $tempModulePath
                    FormatVersion                                 = $FormatVersion
                    ReleaseNotes                                  = $($ReleaseNotes -join "`r`n")
                    Tags                                          = $Tags
                    SkipAutomaticTags                             = $SkipAutomaticTags
                    LicenseUri                                    = $LicenseUri
                    IconUri                                       = $IconUri
                    ProjectUri                                    = $ProjectUri
                    Verbose                                       = $VerbosePreference
                    WarningAction                                 = $WarningPreference
                    ErrorAction                                   = $ErrorActionPreference
                    Debug                                         = $DebugPreference
                }
                if ($PSBoundParameters.Containskey('Credential')) { $PublishPSArtifactUtility_Params.Add('Credential', $Credential) }
                if ($Exclude) { $PublishPSArtifactUtility_Params.Add('Exclude', $Exclude) }
                Publish-PSArtifactUtility @PublishPSArtifactUtility_Params
            }
        } finally { Microsoft.PowerShell.Management\Remove-Item $tempModulePath -Force -Recurse -ErrorAction Ignore -WarningAction SilentlyContinue -Confirm:$false -WhatIf:$false }
    }
}
function Update-MarkdownHelpModule {
    [CmdletBinding()]
    [OutputType([System.IO.FileInfo[]])]
    param([Parameter(Mandatory = $true,
            ValueFromPipeline = $true)]
        [SupportsWildcards()]
        [string[]]$Path,
        [System.Text.Encoding]$Encoding = $script:UTF8_NO_BOM,
        [switch]$RefreshModulePage,
        [string]$ModulePagePath,
        [string]$LogPath,
        [switch]$LogAppend,
        [switch]$AlphabeticParamsOrder,
        [switch]$UseFullTypeName,
        [switch]$UpdateInputOutput,
        [switch]$Force,
        [System.Management.Automation.Runspaces.PSSession]$Session,
        [switch]$ExcludeDontShow)
    begin {
        validateWorkingProvider
        $infoCallback = GetInfoCallback $LogPath -Append:$LogAppend
        $MarkdownFiles = @()
    }
    process
    { }
    end {
        function log {
            param([string]$message,
                [switch]$warning)
            $message = "[Update-MarkdownHelpModule] $([datetime]::now) $message"
            if ($warning)
            { Write-Warning $message }
            $infoCallback.Invoke($message)
        }
        foreach ($modulePath in $Path) {
            $module = $null
            $h = Get-MarkdownMetadata -Path $modulePath
            if ($h.$script:MODULE_PAGE_MODULE_NAME) {
                $module = $h.$script:MODULE_PAGE_MODULE_NAME | Select-Object -First 1
                log ($LocalizedData.ModuleNameFromPath -f $modulePath, $module)
            }
            if (-not $module) {
                Write-Error -Message ($LocalizedData.ModuleNameNotFoundFromPath -f $modulePath)
                continue
            }
            log ("[Update-MarkdownHelpModule]" + (Get-Date).ToString())
            log ($LocalizedData.UpdateDocsForModule -f $module, $modulePath)
            $affectedFiles = Update-MarkdownHelp -Session $Session -Path $modulePath -LogPath $LogPath -LogAppend -Encoding $Encoding -AlphabeticParamsOrder:$AlphabeticParamsOrder -UseFullTypeName:$UseFullTypeName -UpdateInputOutput:$UpdateInputOutput -Force:$Force -ExcludeDontShow:$ExcludeDontShow
            $affectedFiles
            $allCommands = GetCommands -AsNames -Module $Module
            if (-not $allCommands)
            { throw $LocalizedData.ModuleOrCommandNotFound -f $Module }
            $updatedCommands = $affectedFiles.BaseName
            $allCommands | ForEach-Object { if (-not ($updatedCommands -contains $_)) {
                    log ($LocalizedData.CreatingNewMarkdownForCommand -f $_)
                    $newFiles = New-MarkdownHelp -Command $_ -OutputFolder $modulePath -AlphabeticParamsOrder:$AlphabeticParamsOrder -Force:$Force -ExcludeDontShow:$ExcludeDontShow
                    $newFiles
                } }
            if ($RefreshModulePage) {
                $MamlModel = New-Object System.Collections.Generic.List[Markdown.MAML.Model.MAML.MamlCommand]
                $files = @()
                $MamlModel = GetMamlModelImpl $affectedFiles -ForAnotherMarkdown -Encoding $Encoding
                NewModuleLandingPage -RefreshModulePage -ModulePagePath $ModulePagePath -Path $modulePath -ModuleName $module -Module $MamlModel -Encoding $Encoding -Force
            }
        }
    }
}
function Find-Module {
    <#
    .ExternalHelp PSModule-help.xml
    #>

    [CmdletBinding(HelpUri = 'https://go.microsoft.com/fwlink/?LinkID=398574')]
    [outputtype("PSCustomObject[]")]
    Param
    ([Parameter(ValueFromPipelineByPropertyName = $true,
            Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string[]]
        $Name,
        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNull()]
        [string]
        $MinimumVersion,
        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNull()]
        [string]
        $MaximumVersion,
        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNull()]
        [string]
        $RequiredVersion,
        [Parameter()]
        [switch]
        $AllVersions,
        [Parameter()]
        [switch]
        $IncludeDependencies,
        [Parameter()]
        [ValidateNotNull()]
        [string]
        $Filter,
        [Parameter()]
        [ValidateNotNull()]
        [string[]]
        $Tag,
        [Parameter()]
        [ValidateNotNull()]
        [ValidateSet('DscResource', 'Cmdlet', 'Function', 'RoleCapability')]
        [string[]]
        $Includes,
        [Parameter()]
        [ValidateNotNull()]
        [string[]]
        $DscResource,
        [Parameter()]
        [ValidateNotNull()]
        [string[]]
        $RoleCapability,
        [Parameter()]
        [ValidateNotNull()]
        [string[]]
        $Command,
        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [Uri]
        $Proxy,
        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [PSCredential]
        $ProxyCredential,
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string[]]
        $Repository,
        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [PSCredential]
        $Credential,
        [Parameter()]
        [switch]
        $AllowPrerelease)
    Begin { Install-NuGetClientBinaries -CallerPSCmdlet $PSCmdlet -Proxy $Proxy -ProxyCredential $ProxyCredential }
    Process {
        $ValidationResult = Validate-VersionParameters -CallerPSCmdlet $PSCmdlet -Name $Name -MinimumVersion $MinimumVersion -MaximumVersion $MaximumVersion -RequiredVersion $RequiredVersion -AllVersions:$AllVersions -AllowPrerelease:$AllowPrerelease
        if (-not $ValidationResult) { return }
        $PSBoundParameters["Provider"] = $script:PSModuleProviderName
        $PSBoundParameters[$script:PSArtifactType] = $script:PSArtifactTypeModule
        if ($AllowPrerelease) { $PSBoundParameters[$script:AllowPrereleaseVersions] = $true }
        $null = $PSBoundParameters.Remove("AllowPrerelease")
        if ($PSBoundParameters.ContainsKey("Repository")) {
            $PSBoundParameters["Source"] = $Repository
            $null = $PSBoundParameters.Remove("Repository")
            $ev = $null
            $null = Get-PSRepository -Name $Repository -ErrorVariable ev -verbose:$false
            if ($ev) { return }
        }
        $PSBoundParameters["MessageResolver"] = $script:PackageManagementMessageResolverScriptBlock
        $modulesFoundInPSGallery = @()
        $isRepositoryNullOrPSGallerySpecified = $false
        if ($Repository -and ($Repository -Contains $Script:PSGalleryModuleSource)) { $isRepositoryNullOrPSGallerySpecified = $true }
        elseif (-not $Repository) {
            $psgalleryRepo = Get-PSRepository -Name $Script:PSGalleryModuleSource -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
            if ($psgalleryRepo) { $isRepositoryNullOrPSGallerySpecified = $true }
        }
        PackageManagement\Find-Package @PSBoundParameters | Microsoft.PowerShell.Core\ForEach-Object { $psgetItemInfo = New-PSGetItemInfo -SoftwareIdentity $_ -Type $script:PSArtifactTypeModule
            if ($psgetItemInfo.Type -eq $script:PSArtifactTypeModule) {
                if ($AllVersions -and -not $AllowPrerelease) { if ($psgetItemInfo.AdditionalMetadata -and $psgetItemInfo.AdditionalMetadata.IsPrerelease -eq 'false') { $psgetItemInfo } }
                else { $psgetItemInfo }
            } elseif ($PSBoundParameters['Name'] -and -not (Test-WildcardPattern -Name ($Name | Microsoft.PowerShell.Core\Where-Object { $psgetItemInfo.Name -like $_ }))) {
                $message = $LocalizedData.MatchInvalidType -f ($psgetItemInfo.Name, $psgetItemInfo.Type, $script:PSArtifactTypeModule)
                Write-Error -Message $message -ErrorId 'MatchInvalidType' -Category InvalidArgument -TargetObject $Name
            }
            if ($psgetItemInfo -and
                $isRepositoryNullOrPSGallerySpecified -and
                $script:TelemetryEnabled -and
                ($psgetItemInfo.Repository -eq $Script:PSGalleryModuleSource)) { $modulesFoundInPSGallery += $psgetItemInfo.Name } }
        if ($isRepositoryNullOrPSGallerySpecified) { Log-ArtifactNotFoundInPSGallery -SearchedName $Name -FoundName $modulesFoundInPSGallery -operationName 'PSGET_FIND_MODULE' }
    }
}
function Find-Script {
    <#
    .ExternalHelp PSModule-help.xml
    #>

    [CmdletBinding(HelpUri = 'https://go.microsoft.com/fwlink/?LinkId=619785')]
    [outputtype("PSCustomObject[]")]
    Param
    ([Parameter(ValueFromPipelineByPropertyName = $true,
            Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string[]]
        $Name,
        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNull()]
        [string]
        $MinimumVersion,
        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNull()]
        [string]
        $MaximumVersion,
        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNull()]
        [string]
        $RequiredVersion,
        [Parameter()]
        [switch]
        $AllVersions,
        [Parameter()]
        [switch]
        $IncludeDependencies,
        [Parameter()]
        [ValidateNotNull()]
        [string]
        $Filter,
        [Parameter()]
        [ValidateNotNull()]
        [string[]]
        $Tag,
        [Parameter()]
        [ValidateNotNull()]
        [ValidateSet('Function', 'Workflow')]
        [string[]]
        $Includes,
        [Parameter()]
        [ValidateNotNull()]
        [string[]]
        $Command,
        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [Uri]
        $Proxy,
        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [PSCredential]
        $ProxyCredential,
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string[]]
        $Repository,
        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [PSCredential]
        $Credential,
        [Parameter()]
        [switch]
        $AllowPrerelease)
    Begin { Install-NuGetClientBinaries -CallerPSCmdlet $PSCmdlet -Proxy $Proxy -ProxyCredential $ProxyCredential }
    Process {
        $ValidationResult = Validate-VersionParameters -CallerPSCmdlet $PSCmdlet -Name $Name -MinimumVersion $MinimumVersion -MaximumVersion $MaximumVersion -RequiredVersion $RequiredVersion -AllVersions:$AllVersions -AllowPrerelease:$AllowPrerelease
        if (-not $ValidationResult) { return }
        $PSBoundParameters['Provider'] = $script:PSModuleProviderName
        $PSBoundParameters[$script:PSArtifactType] = $script:PSArtifactTypeScript
        if ($AllowPrerelease) { $PSBoundParameters[$script:AllowPrereleaseVersions] = $true }
        $null = $PSBoundParameters.Remove("AllowPrerelease")
        if ($PSBoundParameters.ContainsKey("Repository")) {
            $PSBoundParameters["Source"] = $Repository
            $null = $PSBoundParameters.Remove("Repository")
            $ev = $null
            $repositories = Get-PSRepository -Name $Repository -ErrorVariable ev -verbose:$false
            if ($ev) { return }
            $RepositoriesWithoutScriptSourceLocation = $false
            foreach ($repo in $repositories) {
                if (-not $repo.ScriptSourceLocation) {
                    $message = $LocalizedData.ScriptSourceLocationIsMissing -f ($repo.Name)
                    Write-Error -Message $message -ErrorId 'ScriptSourceLocationIsMissing' -Category InvalidArgument -TargetObject $repo.Name -Exception 'System.ArgumentException'
                    $RepositoriesWithoutScriptSourceLocation = $true
                }
            }
            if ($RepositoriesWithoutScriptSourceLocation) { return }
        }
        $PSBoundParameters["MessageResolver"] = $script:PackageManagementMessageResolverScriptBlockForScriptCmdlets
        $scriptsFoundInPSGallery = @()
        $isRepositoryNullOrPSGallerySpecified = $false
        if ($Repository -and ($Repository -Contains $Script:PSGalleryModuleSource)) { $isRepositoryNullOrPSGallerySpecified = $true }
        elseif (-not $Repository) {
            $psgalleryRepo = Get-PSRepository -Name $Script:PSGalleryModuleSource -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
            if ($psgalleryRepo) { $isRepositoryNullOrPSGallerySpecified = $true }
        }
        PackageManagement\Find-Package @PSBoundParameters | Microsoft.PowerShell.Core\ForEach-Object { $psgetItemInfo = New-PSGetItemInfo -SoftwareIdentity $_ -Type $script:PSArtifactTypeScript
            if ($psgetItemInfo.Type -eq $script:PSArtifactTypeScript) {
                if ($AllVersions -and -not $AllowPrerelease) { if ($psgetItemInfo.AdditionalMetadata -and $psgetItemInfo.AdditionalMetadata.IsPrerelease -eq $false) { $psgetItemInfo } }
                else { $psgetItemInfo }
            } elseif ($PSBoundParameters['Name'] -and -not (Test-WildcardPattern -Name ($Name | Microsoft.PowerShell.Core\Where-Object { $psgetItemInfo.Name -like $_ }))) {
                $message = $LocalizedData.MatchInvalidType -f ($psgetItemInfo.Name, $psgetItemInfo.Type, $script:PSArtifactTypeScript)
                Write-Error -Message $message -ErrorId 'MatchInvalidType' -Category InvalidArgument -TargetObject $Name
            }
            if ($psgetItemInfo -and
                $isRepositoryNullOrPSGallerySpecified -and
                $script:TelemetryEnabled -and
                ($psgetItemInfo.Repository -eq $Script:PSGalleryModuleSource)) { $scriptsFoundInPSGallery += $psgetItemInfo.Name } }
        if ($isRepositoryNullOrPSGallerySpecified) { Log-ArtifactNotFoundInPSGallery -SearchedName $Name -FoundName $scriptsFoundInPSGallery -operationName PSGET_FIND_SCRIPT }
    }
}
function Get-MarkdownMetadata {
    [CmdletBinding(DefaultParameterSetName = "FromPath")]
    param([Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            Position = 1,
            ParameterSetName = "FromPath")]
        [SupportsWildcards()]
        [string[]]$Path,
        [Parameter(Mandatory = $true,
            ParameterSetName = "FromMarkdownString")]
        [string]$Markdown)
    process {
        if ($PSCmdlet.ParameterSetName -eq 'FromMarkdownString')
        { return [Markdown.MAML.Parser.MarkdownParser]::GetYamlMetadata($Markdown) }
        else {
            GetMarkdownFilesFromPath $Path -IncludeModulePage | ForEach-Object { $md = Get-Content -Raw $_.FullName
                [Markdown.MAML.Parser.MarkdownParser]::GetYamlMetadata($md) }
        }
    }
}
function Get-PSRepository {
    <#
    .ExternalHelp PSModule-help.xml
    #>

    [CmdletBinding(HelpUri = 'https://go.microsoft.com/fwlink/?LinkID=517127')]
    Param
    ([Parameter(ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [string[]]
        $Name)
    Begin { }
    Process {
        $PSBoundParameters["Provider"] = $script:PSModuleProviderName
        $PSBoundParameters["MessageResolver"] = $script:PackageManagementMessageResolverScriptBlock
        if ($Name) {
            foreach ($sourceName in $Name) {
                $PSBoundParameters["Name"] = $sourceName
                $packageSources = PackageManagement\Get-PackageSource @PSBoundParameters
                $packageSources | Microsoft.PowerShell.Core\ForEach-Object { New-ModuleSourceFromPackageSource -PackageSource $_ }
            }
        } else {
            $packageSources = PackageManagement\Get-PackageSource @PSBoundParameters
            $packageSources | Microsoft.PowerShell.Core\ForEach-Object { New-ModuleSourceFromPackageSource -PackageSource $_ }
        }
    }
}
function Update-MarkdownHelp {
    [CmdletBinding()]
    [OutputType([System.IO.FileInfo[]])]
    param([Parameter(Mandatory = $true,
            ValueFromPipeline = $true)]
        [SupportsWildcards()]
        [string[]]$Path,
        [System.Text.Encoding]$Encoding = $script:UTF8_NO_BOM,
        [string]$LogPath,
        [switch]$LogAppend,
        [switch]$AlphabeticParamsOrder,
        [switch]$UseFullTypeName,
        [switch]$UpdateInputOutput,
        [Switch]$Force,
        [System.Management.Automation.Runspaces.PSSession]$Session,
        [switch]$ExcludeDontShow)
    begin {
        validateWorkingProvider
        $infoCallback = GetInfoCallback $LogPath -Append:$LogAppend
        $MarkdownFiles = @()
    }
    process
    { $MarkdownFiles += GetMarkdownFilesFromPath $Path }
    end {
        function log {
            param([string]$message,
                [switch]$warning)
            $message = "[Update-MarkdownHelp] $([datetime]::now) $message"
            if ($warning)
            { Write-Warning $message }
            $infoCallback.Invoke($message)
        }
        if (-not $MarkdownFiles) {
            log -warning ($LocalizedData.NoMarkdownFiles -f $Path)
            return
        }
        $MarkdownFiles | ForEach-Object { $file = $_
            $filePath = $file.FullName
            $oldModels = GetMamlModelImpl $filePath -ForAnotherMarkdown -Encoding $Encoding
            if ($oldModels.Count -gt 1) {
                log -warning ($LocalizedData.FileContainsMoreThanOneCommand -f $filePath)
                log -warning $LocalizedData.OneCommandPerFile
                return
            }
            $oldModel = $oldModels[0]
            $name = $oldModel.Name
            [Array]$loadedModulesBefore = $(Get-Module | Select-Object -Property Name)
            $command = Get-Command $name -ErrorAction SilentlyContinue
            if (-not $command) {
                if ($Force) {
                    if (Test-Path $filePath) {
                        Remove-Item -Path $filePath -Confirm:$false
                        log -warning ($LocalizedData.CommandNotFoundFileRemoved -f $name, $filePath)
                        return
                    }
                } else {
                    log -warning ($LocalizedData.CommandNotFoundSkippingFile -f $name, $filePath)
                    return
                }
            } elseif (($null -ne $command.ModuleName) -and ($loadedModulesBefore.Name -notcontains $command.ModuleName))
            { log -warning ($LocalizedData.ModuleImporteAutomaticaly -f $($command.ModuleName)) }
            $metadata = Get-MarkdownMetadata $filePath
            $metadata["external help file"] = GetHelpFileName $command
            $reflectionModel = GetMamlObject -Session $Session -Cmdlet $name -UseFullTypeName:$UseFullTypeName -ExcludeDontShow:$ExcludeDontShow.IsPresent
            $metadata[$script:MODULE_PAGE_MODULE_NAME] = $reflectionModel.ModuleName
            $merger = New-Object Markdown.MAML.Transformer.MamlModelMerger -ArgumentList $infoCallback
            $newModel = $merger.Merge($reflectionModel, $oldModel, $UpdateInputOutput)
            if ($AlphabeticParamsOrder)
            { SortParamsAlphabetically $newModel }
            $md = ConvertMamlModelToMarkdown -mamlCommand $newModel -metadata $metadata -PreserveFormatting
            MySetContent -path $file.FullName -value $md -Encoding $Encoding -Force }
    }
}
function Add-Directory {
    [CmdletBinding()]
    param([string] $Directory)
    $exists = Test-Path -Path $Directory
    if ($exists -eq $false) { $null = New-Item -Path $Directory -ItemType Directory -Force }
}
function Add-FilesWithFolders {
    [CmdletBinding()]
    param ($file, $FullProjectPath, $directory)
    $LinkPrivatePublicFiles = foreach ($dir in $directory) { if ($file -like "$dir*") { $file } }
    $LinkPrivatePublicFiles
}
function Copy-File {
    [CmdletBinding()]
    param ($Source,
        $Destination)
    if ((Test-Path $Source) -and !(Test-Path $Destination)) { Copy-Item -Path $Source -Destination $Destination }
}
function Export-PSData {
    [cmdletbinding()]
    <#
    .Synopsis
        Exports property bags into a data file
    .Description
        Exports property bags and the first level of any other object into a ps data file (.psd1)
    .Link
        https://github.com/StartAutomating/Pipeworks
        Import-PSData
    .Example
        Get-Web -Url http://www.youtube.com/watch?v=xPRC3EDR_GU -AsMicrodata -ItemType http://schema.org/VideoObject |
            Export-PSData .\PipeworksQuickstart.video.psd1
    #>

    [OutputType([IO.FileInfo])]
    param([Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [PSObject[]]
        $InputObject,
        [Parameter(Mandatory = $true, Position = 0)]
        [string]
        $DataFile)
    begin { $AllObjects = New-Object Collections.ArrayList }
    process { $null = $AllObjects.AddRange($InputObject) }
    end {
        $text = $AllObjects |
            Write-PowerShellHashtable
        $text |
            Set-Content -Path $DataFile
        Get-Item -Path $DataFile
    }
}
function Find-EnumsList {
    [CmdletBinding()]
    param ([string] $ProjectPath)
    if ($PSEdition -eq 'Core') { $Enums = Get-ChildItem -Path $ProjectPath\Enums\*.ps1 -ErrorAction SilentlyContinue -FollowSymlink } else { $Enums = Get-ChildItem -Path $ProjectPath\Enums\*.ps1 -ErrorAction SilentlyContinue }
    $EnumsList = @($Files = Foreach ($import in @($Enums)) { "'Enums\$($import.Name)'" }
        $Files -join ',')
    return [string] "@($EnumsList)"
}
function Find-RequiredModules {
    param([string] $Name)
    $Module = Get-Module -ListAvailable $Name -ErrorAction SilentlyContinue -Verbose:$false
    $AllModules = if ($Module) {
        [Array] $RequiredModules = $Module.RequiredModules.Name
        if ($null -ne $RequiredModules) { $null }
        $RequiredModules
        foreach ($_ in $RequiredModules) { Find-RequiredModules -Path $Path -Name $_ }
    }
    [Array] $ListModules = $AllModules | Where-Object { $null -ne $_ }
    if ($null -ne $ListModules) { [array]::Reverse($ListModules) }
    $CleanedModules = [System.Collections.Generic.List[string]]::new()
    foreach ($_ in $ListModules) { if ($CleanedModules -notcontains $_) { $CleanedModules.Add($_) } }
    $CleanedModules
}
function Format-Code {
    [cmdletbinding()]
    param([string] $FilePath,
        $FormatCode)
    if ($FormatCode.Enabled) {
        if ($FormatCode.RemoveComments) { $Output = Write-TextWithTime -Text "[+] Removing Comments - $FilePath" { Remove-Comments -FilePath $FilePath } } else { $Output = Write-TextWithTime -Text "[+] Reading file content - $FilePath" { Get-Content -LiteralPath $FilePath -Raw } }
        if ($null -eq $FormatCode.FormatterSettings) { $FormatCode.FormatterSettings = $Script:FormatterSettings }
        $Data = Write-TextWithTime -Text "[+] Formatting file - $FilePath" { try { Invoke-Formatter -ScriptDefinition $Output -Settings $FormatCode.FormatterSettings -Verbose:$false } catch {
                $ErrorMessage = $_.Exception.Message
                Write-Error "Format-Code - Formatting on file $FilePath failed. Error: $ErrorMessage"
                Exit
            } }
        Write-TextWithTime -Text "[+] Saving file - $FilePath" { $Final = foreach ($O in $Data) { if ($O.Trim() -ne '') { $O.Trim() } }
            try { $Final | Out-File -LiteralPath $FilePath -NoNewline -Encoding utf8 } catch {
                $ErrorMessage = $_.Exception.Message
                Write-Text "[-] Format-Code - Resaving file $FilePath failed. Error: $ErrorMessage" -Color Red
                Exit
            } }
    }
}
function Format-PSD1 {
    [cmdletbinding()]
    param([string] $PSD1FilePath,
        $FormatCode)
    if ($FormatCode.Enabled) {
        $Output = Get-Content -LiteralPath $PSD1FilePath -Raw
        if ($FormatCode.RemoveComments) {
            Write-Verbose "Removing Comments - $PSD1FilePath"
            $Output = Remove-Comments -ScriptContent $Output
        }
        Write-Verbose "Formatting - $PSD1FilePath"
        if ($null -eq $FormatCode.FormatterSettings) { $FormatCode.FormatterSettings = $Script:FormatterSettings }
        $Output = Invoke-Formatter -ScriptDefinition $Output -Settings $FormatCode.FormatterSettings
        $Output | Out-File -LiteralPath $PSD1FilePath -NoNewline
    }
}
function Format-UsingNamespace {
    [CmdletBinding()]
    param([string] $FilePath,
        [string] $FilePathSave,
        [string] $FilePathUsing)
    if ($FilePathSave -eq '') { $FilePathSave = $FilePath }
    if ($FilePath -ne '' -and (Test-Path -Path $FilePath) -and (Get-Item -LiteralPath $FilePath).Length -gt 0kb) {
        $FileStream = New-Object -TypeName IO.FileStream -ArgumentList ($FilePath), ([System.IO.FileMode]::Open), ([System.IO.FileAccess]::Read), ([System.IO.FileShare]::ReadWrite)
        $ReadFile = New-Object -TypeName System.IO.StreamReader -ArgumentList ($FileStream, [System.Text.Encoding]::UTF8, $true)
        $UsingNamespaces = [System.Collections.Generic.List[string]]::new()
        $Content = while (!$ReadFile.EndOfStream) {
            $Line = $ReadFile.ReadLine()
            if ($Line -like 'using namespace*') { $UsingNamespaces.Add($Line) } else { $Line }
        }
        $ReadFile.Close()
        $null = New-Item -Path $FilePathSave -ItemType file -Force
        if ($UsingNamespaces) {
            $null = New-Item -Path $FilePathUsing -ItemType file -Force
            $UsingNamespaces = $UsingNamespaces.Trim() | Sort-Object -Unique
            $UsingNamespaces | Add-Content -LiteralPath $FilePathUsing -Encoding utf8
            $Content | Add-Content -LiteralPath $FilePathSave -Encoding utf8
            return $true
        } else {
            $Content | Add-Content -LiteralPath $FilePathSave -Encoding utf8
            return $False
        }
    }
}
Function Get-AliasTarget {
    [cmdletbinding()]
    param ([Alias('PSPath', 'FullName')][Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)][string[]]$Path,
        [string] $Content,
        [switch] $RecurseFunctionNames)
    process {
        if ($Content) {
            $ProcessData = $Content
            $Code = $true
        } else {
            $ProcessData = $Path
            $Code = $false
        }
        foreach ($File in $ProcessData) {
            $Ast = $null
            if ($Code) { $FileAst = [System.Management.Automation.Language.Parser]::ParseInput($File, [ref]$null, [ref]$null) } else { $FileAst = [System.Management.Automation.Language.Parser]::ParseFile($File , [ref]$null, [ref]$null) }
            $FunctionName = $FileAst.FindAll( { param ($ast)
                    $ast -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $RecurseFunctionNames).Name
            $Ast = $Null
            $AliasDefinitions = $FileAst.FindAll( { param ($ast)
                    $ast -is [System.Management.Automation.Language.AttributeAst] -and
                    $ast.TypeName.Name -eq 'Alias' -and
                    $ast.Parent -is [System.Management.Automation.Language.ParamBlockAst] }, $true)
            $AliasTarget = @($AliasDefinitions.PositionalArguments.Value
                foreach ($_ in $AliasDefinitions.Parent.CommandElements) { if ($_.StringConstantType -eq 'BareWord' -and $_.Value -notin ('New-Alias', 'Set-Alias', $FunctionName)) { $_.Value } })
            $AliasTarget = foreach ($_ in $AliasTarget) { if ($_ -ne $null) { $_ } }
            [PsCustomObject]@{Function = $FunctionName
                Alias                  = $AliasTarget
            }
        }
    }
}
function Get-FilteredScriptCommands {
    [CmdletBinding()]
    param([Array] $Commands,
        [switch] $NotCmdlet,
        [switch] $NotUnknown,
        [switch] $NotApplication,
        [string[]] $Functions)
    if ($Functions.Count -eq 0) { $Functions = Get-FunctionNames -Path $FilePath }
    $Commands = $Commands | Where-Object { $_ -notin $Functions }
    $Commands = $Commands | Sort-Object -Unique
    $Scan = foreach ($Command in $Commands) {
        try {
            $Data = Get-Command -Name $Command -ErrorAction Stop
            [PSCustomObject] @{Name = $Data.Name
                Source              = $Data.Source
                CommandType         = $Data.CommandType
                Error               = ''
                ScriptBlock         = $Data.ScriptBlock
            }
        } catch {
            [PSCustomObject] @{Name = $Command
                Source              = ''
                CommandType         = ''
                Error               = $_.Exception.Message
                ScriptBlock         = ''
            }
        }
    }
    $Filtered = foreach ($Command in $Scan) { if ($NotCmdlet -and $NotUnknown -and $NotApplication) { if ($Command.CommandType -ne 'Cmdlet' -and $Command.Source -ne '' -and $Command.CommandType -ne 'Application') { $Command } } elseif ($NotCmdlet -and $NotUnknown) { if ($Command.CommandType -ne 'Cmdlet' -and $Command.Source -ne '') { $Command } } elseif ($NotCmdlet) { if ($Command.CommandType -ne 'Cmdlet') { $Command } } elseif ($NotUnknown) { if ($Command.Source -ne '') { $Command } } elseif ($NotApplication) { if ($Command.CommandType -ne 'Application') { $Command } } else { $Command } }
    $Filtered
}
function Get-FunctionAliasesFromFolder {
    [cmdletbinding()]
    param([string] $FullProjectPath,
        [string[]] $Folder,
        [Array] $Files)
    $FilesPS1 = foreach ($File in $Files) { if ($file.FullName -like "*\Public\*") { if ($File.Extension -eq '.ps1' -or $File.Extension -eq '*.psm1') { $File } } }
    [Array] $Content = foreach ($File in $FilesPS1) {
        ''
        Get-Content -LiteralPath $File.FullName -Raw -Encoding Default
    }
    $Code = $Content -join [System.Environment]::NewLine
    $AliasesToExport = Get-AliasTarget -Content $Code
    $AliasesToExport
}
function Get-FunctionNames {
    [cmdletbinding()]
    param([string] $Path,
        [switch] $Recurse)
    [System.Management.Automation.Language.Parser]::ParseFile((Resolve-Path $Path),
        [ref]$null,
        [ref]$null).FindAll( { param($c)$c -is [Management.Automation.Language.FunctionDefinitionAst] }, $Recurse).Name
}
function Get-RecursiveCommands {
    [CmdletBinding()]
    param([Array] $Commands)
    $Another = foreach ($Command in $Commands) { if ($Command.ScriptBlock) { Get-ScriptCommands -Code $Command.ScriptBlock -CommandsOnly } }
    $filter = Get-FilteredScriptCommands -Commands $Another -NotUnknown -NotCmdlet
    [Array] $ProcessedCommands = foreach ($_ in $Filter) {
        if ($_.Name -notin $ListCommands.Name) {
            $ListCommands.Add($_)
            $_
        }
    }
    if ($ProcessedCommands.Count -gt 0) { Get-RecursiveCommands -Commands $ProcessedCommands }
}
function Merge-Module {
    [CmdletBinding()]
    param ([string] $ModuleName,
        [string] $ModulePathSource,
        [string] $ModulePathTarget,
        [Parameter(Mandatory = $false, ValueFromPipeline = $false)]
        [ValidateSet("ASC", "DESC", "NONE", '')]
        [string] $Sort = 'NONE',
        [string[]] $FunctionsToExport,
        [string[]] $AliasesToExport,
        [Array] $LibrariesCore,
        [Array] $LibrariesDefault,
        [System.Collections.IDictionary] $FormatCodePSM1,
        [System.Collections.IDictionary] $FormatCodePSD1,
        [System.Collections.IDictionary] $Configuration)
    $TimeToExecute = [System.Diagnostics.Stopwatch]::StartNew()
    Write-Text "[+] 1st stage merging" -Color Blue
    $PSM1FilePath = "$ModulePathTarget\$ModuleName.psm1"
    $PSD1FilePath = "$ModulePathTarget\$ModuleName.psd1"
    if ($PSEdition -eq 'Core') { $ScriptFunctions = Get-ChildItem -Path $ModulePathSource\*.ps1 -ErrorAction SilentlyContinue -Recurse -FollowSymlink } else { $ScriptFunctions = Get-ChildItem -Path $ModulePathSource\*.ps1 -ErrorAction SilentlyContinue -Recurse }
    if ($Sort -eq 'ASC') { $ScriptFunctions = $ScriptFunctions | Sort-Object -Property Name } elseif ($Sort -eq 'DESC') { $ScriptFunctions = $ScriptFunctions | Sort-Object -Descending -Property Name }
    foreach ($FilePath in $ScriptFunctions) {
        $Content = Get-Content -Path $FilePath -Raw
        $Content = $Content.Replace('$PSScriptRoot\', '$PSScriptRoot\')
        $Content = $Content.Replace('$PSScriptRoot\', '$PSScriptRoot\')
        try { $Content | Out-File -Append -LiteralPath $PSM1FilePath -Encoding utf8 } catch {
            $ErrorMessage = $_.Exception.Message
            Write-Error "Merge-Module - Merge on file $FilePath failed. Error: $ErrorMessage"
            Exit
        }
    }
    $FilePathUsing = "$ModulePathTarget\$ModuleName.Usings.ps1"
    $UsingInPlace = Format-UsingNamespace -FilePath $PSM1FilePath -FilePathUsing $FilePathUsing
    if ($UsingInPlace) {
        Format-Code -FilePath $FilePathUsing -FormatCode $FormatCodePSM1
        $Configuration.UsingInPlace = "$ModuleName.Usings.ps1"
    }
    $TimeToExecute.Stop()
    Write-Text "[+] 1st stage merging [Time: $($($TimeToExecute.Elapsed).Tostring())]" -Color Blue
    $TimeToExecute = [System.Diagnostics.Stopwatch]::StartNew()
    Write-Text "[+] 2nd stage missing functions" -Color Blue
    $ApprovedModules = $Configuration.Options.Merge.Integrate.ApprovedModules
    $MissingFunctions = Get-MissingFunctions -FilePath $PSM1FilePath -SummaryWithCommands
    $TimeToExecute.Stop()
    Write-Text "[+] 2nd stage missing functions [Time: $($($TimeToExecute.Elapsed).Tostring())]" -Color Blue
    $TimeToExecute = [System.Diagnostics.Stopwatch]::StartNew()
    Write-Text "[+] 3rd stage required modules" -Color Blue
    $RequiredModules = @(if ($Configuration.Information.Manifest.RequiredModules[0] -is [System.Collections.IDictionary]) { $Configuration.Information.Manifest.RequiredModules.ModuleName } else { $Configuration.Information.Manifest.RequiredModules })
    $DependantRequiredModules = foreach ($_ in $RequiredModules) { Find-RequiredModules -Name $_ }
    $DependantRequiredModules = $DependantRequiredModules | Sort-Object -Unique
    $TimeToExecute.Stop()
    Write-Text "[+] 3rd stage required modules [Time: $($($TimeToExecute.Elapsed).Tostring())]" -Color Blue
    $TimeToExecute = [System.Diagnostics.Stopwatch]::StartNew()
    Write-Text "[+] 4th stage commands used" -Color Blue
    foreach ($Module in $MissingFunctions.Summary.Source | Sort-Object -Unique) {
        if ($Module -in $RequiredModules -and $Module -in $ApprovedModules) {
            Write-Text "[+] Module $Module is in required modules with ability to merge." -Color Green
            $MyFunctions = ($MissingFunctions.Summary | Where-Object { $_.Source -eq $Module }).Name
            foreach ($F in $MyFunctions) { Write-Text " [>] Command used $F" -Color Yellow }
        } elseif ($Module -in $DependantRequiredModules -and $Module -in $ApprovedModules) {
            Write-Text "[+] Module $Module is in dependant required module within required modules with ability to merge." -Color Green
            $MyFunctions = ($MissingFunctions.Summary | Where-Object { $_.Source -eq $Module }).Name
            foreach ($F in $MyFunctions) { Write-Text " [>] Command used $F" -Color Yellow }
        } elseif ($Module -in $DependantRequiredModules) {
            Write-Text "[+] Module $Module is in dependant required module within required modules." -Color Green
            $MyFunctions = ($MissingFunctions.Summary | Where-Object { $_.Source -eq $Module }).Name
            foreach ($F in $MyFunctions) { Write-Text " [>] Command used $F" -Color Green }
        } elseif ($Module -in $RequiredModules) {
            Write-Text "[+] Module $Module is in required modules." -Color Green
            $MyFunctions = ($MissingFunctions.Summary | Where-Object { $_.Source -eq $Module }).Name
            foreach ($F in $MyFunctions) { Write-Text " [>] Command used $F" -Color Green }
        } else {
            Write-Text "[-] Module $Module is missing in required modules. Potential issue." -Color Red
            $MyFunctions = ($MissingFunctions.Summary | Where-Object { $_.Source -eq $Module }).Name
            foreach ($F in $MyFunctions) { Write-Text " [>] Command affected $F" -Color Red }
        }
    }
    $TimeToExecute.Stop()
    Write-Text "[+] 4th stage commands used [Time: $($($TimeToExecute.Elapsed).Tostring())]" -Color Blue
    if ($Configuration.Steps.BuildModule.MergeMissing -eq $true) {
        $TimeToExecute = [System.Diagnostics.Stopwatch]::StartNew()
        Write-Text "[+] 5th stage merge mergable commands" -Color Blue
        $PSM1Content = Get-Content -LiteralPath $PSM1FilePath -Raw
        $IntegrateContent = @($MissingFunctions.Functions
            $PSM1Content)
        $IntegrateContent | Set-Content -LiteralPath $PSM1FilePath -Encoding UTF8
        $NewRequiredModules = foreach ($_ in $Configuration.Information.Manifest.RequiredModules) { if ($_ -is [System.Collections.IDictionary]) { if ($_.ModuleName -notin $ApprovedModules) { $_ } } else { if ($_ -notin $ApprovedModules) { $_ } } }
        $Configuration.Information.Manifest.RequiredModules = $NewRequiredModules
        $TimeToExecute.Stop()
        Write-Text "[+] 5th stage merge mergable commands [Time: $($($TimeToExecute.Elapsed).Tostring())]" -Color Blue
    }
    New-PSMFile -Path $PSM1FilePath -FunctionNames $FunctionsToExport -FunctionAliaes $AliasesToExport -LibrariesCore $LibrariesCore -LibrariesDefault $LibrariesDefault -ModuleName $ModuleName -UsingNamespaces:$UsingInPlace
    Format-Code -FilePath $PSM1FilePath -FormatCode $FormatCodePSM1
    New-PersonalManifest -Configuration $Configuration -ManifestPath $PSD1FilePath -AddUsingsToProcess
    Format-Code -FilePath $PSD1FilePath -FormatCode $FormatCodePSD1
    Get-ChildItem $ModulePathTarget -Recurse -Force -Directory | Sort-Object -Property FullName -Descending | Where-Object { $($_ | Get-ChildItem -Force | Select-Object -First 1).Count -eq 0 } | Remove-Item
}
function New-CreateModule {
    [CmdletBinding()]
    param ([string] $ProjectName,
        [string] $ModulePath,
        [string] $ProjectPath)
    $FullProjectPath = "$projectPath\$projectName"
    $Folders = 'Private', 'Public', 'Examples', 'Ignore', 'Publish', 'Enums', 'Data'
    Add-Directory $FullProjectPath
    foreach ($folder in $Folders) { Add-Directory "$FullProjectPath\$folder" }
    Copy-File -Source "$PSScriptRoot\Data\Example-Gitignore.txt" -Destination "$FullProjectPath\.gitignore"
    Copy-File -Source "$PSScriptRoot\Data\Example-LicenseMIT.txt" -Destination "$FullProjectPath\License"
    Copy-File -Source "$PSScriptRoot\Data\Example-ModuleStarter.ps1" -Destination "$FullProjectPath\$ProjectName.psm1"
}
function New-GitHubRelease {
    <#
    .SYNOPSIS
    Creates a new Release for the given GitHub repository.
 
    .DESCRIPTION
    Uses the GitHub API to create a new Release for a given repository.
    Allows you to specify all of the Release properties, such as the Tag, Name, Assets, and if it's a Draft or Prerelease or not.
 
    .PARAMETER GitHubUsername
    The username that the GitHub repository exists under.
    e.g. For the repository https://github.com/deadlydog/New-GitHubRelease, the username is 'deadlydog'.
 
    .PARAMETER GitHubRepositoryName
    The name of the repository to create the Release for.
    e.g. For the repository https://github.com/deadlydog/New-GitHubRelease, the repository name is 'New-GitHubRelease'.
 
    .PARAMETER GitHubAccessToken
    The Access Token to use as credentials for GitHub.
    Access tokens can be generated at https://github.com/settings/tokens.
    The access token will need to have the repo/public_repo permission on it for it to be allowed to create a new Release.
 
    .PARAMETER TagName
    The name of the tag to create at the Commitish.
 
    .PARAMETER ReleaseName
    The name to use for the new release.
    If blank, the TagName will be used.
 
    .PARAMETER ReleaseNotes
    The text describing the contents of the release.
 
    .PARAMETER AssetFilePaths
    The full paths of the files to include in the release.
 
    .PARAMETER Commitish
    Specifies the commitish value that determines where the Git tag is created from.
    Can be any branch or commit SHA. Unused if the Git tag already exists.
    Default: the repository's default branch (usually master).
 
    .PARAMETER IsDraft
    True to create a draft (unpublished) release, false to create a published one.
    Default: false
 
    .PARAMETER IsPreRelease
    True to identify the release as a prerelease. false to identify the release as a full release.
    Default: false
 
    .OUTPUTS
    A hash table with the following properties is returned:
 
    Succeeded = $true if the Release was created successfully and all assets were uploaded to it, $false if some part of the process failed.
    ReleaseCreationSucceeded = $true if the Release was created successfully (does not include asset uploads), $false if the Release was not created.
    AllAssetUploadsSucceeded = $true if all assets were uploaded to the Release successfully, $false if one of them failed, $null if there were no assets to upload.
    ReleaseUrl = The URL of the new Release that was created.
    ErrorMessage = A message describing what went wrong in the case that Succeeded is $false.
 
    .EXAMPLE
    # Import the module dynamically from the PowerShell Gallery. Use CurrentUser scope to avoid having to run as admin.
    Import-Module -Name New-GitHubRelease -Scope CurrentUser
 
    # Specify the parameters required to create the release. Do it as a hash table for easier readability.
    $newGitHubReleaseParameters =
    @{
        GitHubUsername = 'deadlydog'
        GitHubRepositoryName = 'New-GitHubRelease'
        GitHubAccessToken = 'SomeLongHexidecimalString'
        ReleaseName = "New-GitHubRelease v1.0.0"
        TagName = "v1.0.0"
        ReleaseNotes = "This release contains the following changes: ..."
        AssetFilePaths = @('C:\MyProject\Installer.exe','C:\MyProject\Documentation.md')
        IsPreRelease = $false
        IsDraft = $true # Set to true when testing so we don't publish a real release (visible to everyone) by accident.
    }
 
    # Try to create the Release on GitHub and save the results.
    $result = New-GitHubRelease @newGitHubReleaseParameters
 
    # Provide some feedback to the user based on the results.
    if ($result.Succeeded -eq $true)
    {
        Write-Output "Release published successfully! View it at $($result.ReleaseUrl)"
    }
    elseif ($result.ReleaseCreationSucceeded -eq $false)
    {
        Write-Error "The release was not created. Error message is: $($result.ErrorMessage)"
    }
    elseif ($result.AllAssetUploadsSucceeded -eq $false)
    {
        Write-Error "The release was created, but not all of the assets were uploaded to it. View it at $($result.ReleaseUrl). Error message is: $($result.ErrorMessage)"
    }
 
    Attempt to create a new Release on GitHub, and provide feedback to the user indicating if it succeeded or not.
 
    .LINK
    Project home: https://github.com/deadlydog/New-GitHubRelease
 
    .NOTES
    Name: New-GitHubRelease
    Author: Daniel Schroeder (originally based on the script at https://github.com/majkinetor/au/blob/master/scripts/Github-CreateRelease.ps1)
    GitHub Release API Documentation: https://developer.github.com/v3/repos/releases/#create-a-release
    Version: 1.0.2
#>

    [CmdletBinding()]
    param
    ([Parameter(Mandatory = $true, HelpMessage = "The username the repository is under (e.g. deadlydog).")]
        [string] $GitHubUsername,
        [Parameter(Mandatory = $true, HelpMessage = "The repository name to create the release in (e.g. Invoke-MsBuild).")]
        [string] $GitHubRepositoryName,
        [Parameter(Mandatory = $true, HelpMessage = "The Acess Token to use as credentials for GitHub.")]
        [string] $GitHubAccessToken,
        [Parameter(Mandatory = $true, HelpMessage = "The name of the tag to create at the the Commitish.")]
        [string] $TagName,
        [Parameter(Mandatory = $false, HelpMessage = "The name of the release. If blank, the TagName will be used.")]
        [string] $ReleaseName,
        [Parameter(Mandatory = $false, HelpMessage = "Text describing the contents of the tag.")]
        [string] $ReleaseNotes,
        [Parameter(Mandatory = $false, HelpMessage = "The full paths of the files to include in the release.")]
        [string[]] $AssetFilePaths,
        [Parameter(Mandatory = $false, HelpMessage = "Specifies the commitish value that determines where the Git tag is created from. Can be any branch or commit SHA. Unused if the Git tag already exists. Default: the repository's default branch (usually master).")]
        [string] $Commitish,
        [Parameter(Mandatory = $false, HelpMessage = "True to create a draft (unpublished) release, false to create a published one. Default: false")]
        [bool] $IsDraft = $false,
        [Parameter(Mandatory = $false, HelpMessage = "True to identify the release as a prerelease. false to identify the release as a full release. Default: false")]
        [bool] $IsPreRelease = $false)
    BEGIN {
        Set-StrictMode -Version Latest
        Set-SecurityProtocolForThread
        [string] $NewLine = [Environment]::NewLine
        if ([string]::IsNullOrEmpty($ReleaseName)) { $ReleaseName = $TagName }
        Test-AllFilePathsAndThrowErrorIfOneIsNotValid $AssetFilePaths
    }
    END { }
    PROCESS {
        $result = @{ }
        $result.Succeeded = $false
        $result.ReleaseCreationSucceeded = $false
        $result.AllAssetUploadsSucceeded = $false
        $result.ReleaseUrl = $null
        $result.ErrorMessage = $null
        [bool] $thereAreNoAssetsToIncludeInTheRelease = ($AssetFilePaths -eq $null) -or ($AssetFilePaths.Count -le 0)
        if ($thereAreNoAssetsToIncludeInTheRelease) { $result.AllAssetUploadsSucceeded = $null }
        $authHeader =
        @{Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes($GitHubAccessToken + ":x-oauth-basic")) }
        $releaseData =
        @{tag_name           = $TagName
            target_commitish = $Commitish
            name             = $ReleaseName
            body             = $ReleaseNotes
            draft            = $IsDraft
            prerelease       = $IsPreRelease
        }
        $createReleaseWebRequestParameters =
        @{Uri           = "https://api.github.com/repos/$GitHubUsername/$GitHubRepositoryName/releases"
            Method      = 'POST'
            Headers     = $authHeader
            ContentType = 'application/json'
            Body        = (ConvertTo-Json $releaseData -Compress)
        }
        try {
            Write-Verbose "Sending web request to create the new Release..."
            $createReleaseWebRequestResults = Invoke-RestMethodAndThrowDescriptiveErrorOnFailure $createReleaseWebRequestParameters
        } catch {
            $result.ReleaseCreationSucceeded = $false
            $result.ErrorMessage = $_.Exception.Message
            return $result
        }
        $result.ReleaseCreationSucceeded = $true
        $result.ReleaseUrl = $createReleaseWebRequestResults.html_url
        if ($thereAreNoAssetsToIncludeInTheRelease) {
            $result.Succeeded = $true
            return $result
        }
        [string] $urlToUploadFilesTo = $createReleaseWebRequestResults.upload_url -replace '{.+}'
        try {
            Write-Verbose "Uploading asset files to the new release..."
            Send-FilesToGitHubRelease -filePathsToUpload $AssetFilePaths -urlToUploadFilesTo $urlToUploadFilesTo -authHeader $authHeader
        } catch {
            $result.AllAssetUploadsSucceeded = $false
            $result.ErrorMessage = $_.Exception.Message
            return $result
        }
        $result.AllAssetUploadsSucceeded = $true
        $result.Succeeded = $true
        return $result
    }
}
function Send-FilesToGitHubRelease([string[]] $filePathsToUpload, [string] $urlToUploadFilesTo, $authHeader) {
    [int] $numberOfFilesToUpload = $filePathsToUpload.Count
    [int] $numberOfFilesUploaded = 0
    $filePathsToUpload | ForEach-Object { $filePath = $_
        $fileName = Get-Item $filePath | Select-Object -ExpandProperty Name
        $uploadAssetWebRequestParameters =
        @{Uri           = $urlToUploadFilesTo + "?name=$fileName"
            Method      = 'POST'
            Headers     = $authHeader
            ContentType = 'application/zip'
            InFile      = $filePath
        }
        $numberOfFilesUploaded = $numberOfFilesUploaded + 1
        Write-Verbose "Uploading asset $numberOfFilesUploaded of $numberOfFilesToUpload, '$filePath'."
        Invoke-RestMethodAndThrowDescriptiveErrorOnFailure $uploadAssetWebRequestParameters > $null }
}
function Test-AllFilePathsAndThrowErrorIfOneIsNotValid([string[]] $filePaths) {
    foreach ($filePath in $filePaths) {
        [bool] $fileWasNotFoundAtPath = [string]::IsNullOrEmpty($filePath) -or !(Test-Path -Path $filePath -PathType Leaf)
        if ($fileWasNotFoundAtPath) { throw "There is no file at the specified path, '$filePath'." }
    }
}
function Invoke-RestMethodAndThrowDescriptiveErrorOnFailure($requestParametersHashTable) {
    $requestDetailsAsNicelyFormattedString = Convert-HashTableToNicelyFormattedString $requestParametersHashTable
    Write-Verbose "Making web request with the following parameters:$NewLine$requestDetailsAsNicelyFormattedString"
    try { $webRequestResult = Invoke-RestMethod @requestParametersHashTable } catch {
        [Exception] $exception = $_.Exception
        [string] $errorMessage = Get-RestMethodExceptionDetailsOrNull -restMethodException $exception
        if ([string]::IsNullOrWhiteSpace($errorMessage)) { $errorMessage = $exception.ToString() }
        throw "An unexpected error occurred while making web request:$NewLine$errorMessage"
    }
    Write-Verbose "Web request returned the following result:$NewLine$webRequestResult"
    return $webRequestResult
}
function Get-RestMethodExceptionDetailsOrNull([Exception] $restMethodException) {
    try {
        $responseDetails = @{ResponseUri = $exception.Response.ResponseUri
            StatusCode                   = $exception.Response.StatusCode
            StatusDescription            = $exception.Response.StatusDescription
            ErrorMessage                 = $exception.Message
        }
        [string] $responseDetailsAsNicelyFormattedString = Convert-HashTableToNicelyFormattedString $responseDetails
        [string] $errorInfo = "Request Details:" + $NewLine + $requestDetailsAsNicelyFormattedString
        $errorInfo += $NewLine
        $errorInfo += "Response Details:" + $NewLine + $responseDetailsAsNicelyFormattedString
        return $errorInfo
    } catch { return $null }
}
function Convert-HashTableToNicelyFormattedString($hashTable) {
    [string] $nicelyFormattedString = $hashTable.Keys | ForEach-Object { $key = $_
        $value = $hashTable.$key
        " $key = $value$NewLine" }
    return $nicelyFormattedString
}
function Set-SecurityProtocolForThread { [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 -bor [System.Net.SecurityProtocolType]::Tls11 -bor [System.Net.SecurityProtocolType]::Tls }
function New-PersonalManifest {
    [CmdletBinding()]
    param([System.Collections.IDictionary] $Configuration,
        [string] $ManifestPath,
        [switch] $AddScriptsToProcess,
        [switch] $AddUsingsToProcess)
    $Manifest = $Configuration.Information.Manifest
    $Manifest.Path = $ManifestPath
    if (-not $AddScriptsToProcess) { $Manifest.ScriptsToProcess = @() }
    if ($AddUsingsToProcess -and $Configuration.UsingInPlace) { $Manifest.ScriptsToProcess = @($Configuration.UsingInPlace) }
    New-ModuleManifest @Manifest
    if ($Configuration.Steps.PublishModule.Prerelease -ne '') {
        $Data = Import-PowerShellDataFile -Path $Configuration.Information.Manifest.Path
        if ($Data.ScriptsToProcess.Count -eq 0) { $Data.Remove('ScriptsToProcess') }
        if ($Data.CmdletsToExport.Count -eq 0) { $Data.Remove('CmdletsToExport') }
        $Data.PrivateData.PSData.Prerelease = $Configuration.Steps.PublishModule.Prerelease
        $Data | Export-PSData -DataFile $Configuration.Information.Manifest.Path
    }
    Write-TextWithTime -Text "[+] Converting $($Configuration.Information.Manifest.Path) UTF8 without BOM" { (Get-Content $Manifest.Path) | Out-FileUtf8NoBom $Manifest.Path }
}
function New-PrepareManifest {
    [CmdletBinding()]
    param($ProjectName,
        $modulePath,
        $projectPath,
        $functionToExport,
        $projectUrl)
    Set-Location "$projectPath\$ProjectName"
    $manifest = @{Path    = ".\$ProjectName.psd1"
        RootModule        = "$ProjectName.psm1"
        Author            = 'Przemyslaw Klys'
        CompanyName       = 'Evotec'
        Copyright         = 'Evotec (c) 2011-2019. All rights reserved.'
        Description       = "Simple project"
        FunctionsToExport = $functionToExport
        CmdletsToExport   = ''
        VariablesToExport = ''
        AliasesToExport   = ''
        FileList          = "$ProjectName.psm1", "$ProjectName.psd1"
        HelpInfoURI       = $projectUrl
        ProjectUri        = $projectUrl
    }
    New-ModuleManifest @manifest
}
function New-PSMFile {
    [cmdletbinding()]
    param([string] $Path,
        [string[]] $FunctionNames,
        [string[]] $FunctionAliaes,
        [Array] $LibrariesCore,
        [Array] $LibrariesDefault,
        [string] $ModuleName,
        [switch] $UsingNamespaces)
    try {
        if ($FunctionNames.Count -gt 0) {
            $Functions = ($FunctionNames | Sort-Object -Unique) -join "','"
            $Functions = "'$Functions'"
        } else { $Functions = @() }
        if ($FunctionAliaes.Count -gt 0) {
            $Aliases = ($FunctionAliaes | Sort-Object -Unique) -join "','"
            $Aliases = "'$Aliases'"
        } else { $Aliases = @() }
        "" | Add-Content -Path $Path
        if ($LibrariesCore.Count -gt 0 -and $LibrariesDefault.Count -gt 0) {
            'if ($PSEdition -eq ''Core'') {' | Add-Content -Path $Path
            foreach ($File in $LibrariesCore) {
                $Extension = $File.Substring($File.Length - 4, 4)
                if ($Extension -eq '.dll') {
                    $Output = 'Add-Type -Path $PSScriptRoot\' + $File
                    $Output | Add-Content -Path $Path
                }
            }
            '} else {' | Add-Content -Path $Path
            foreach ($File in $LibrariesDefault) {
                $Extension = $File.Substring($File.Length - 4, 4)
                if ($Extension -eq '.dll') {
                    $Output = 'Add-Type -Path $PSScriptRoot\' + $File
                    $Output | Add-Content -Path $Path
                }
            }
            '}' | Add-Content -Path $Path
        } elseif ($LibrariesCore.Count -gt 0) {
            foreach ($File in $LibrariesCore) {
                $Extension = $File.Substring($File.Length - 4, 4)
                if ($Extension -eq '.dll') {
                    $Output = 'Add-Type -Path $PSScriptRoot\' + $File
                    $Output | Add-Content -Path $Path
                }
            }
        } elseif ($LibrariesDefault.Count -gt 0) {
            foreach ($File in $LibrariesDefault) {
                $Extension = $File.Substring($File.Length - 4, 4)
                if ($Extension -eq '.dll') {
                    $Output = 'Add-Type -Path $PSScriptRoot\' + $File
                    $Output | Add-Content -Path $Path
                }
            }
        }
        @"
 
Export-ModuleMember ``
    -Function @($Functions) ``
    -Alias @($Aliases)
"@
 | Add-Content -Path $Path
    } catch {
        $ErrorMessage = $_.Exception.Message
        Write-Error "New-PSM1File from $ModuleName failed build. Error: $ErrorMessage"
        Exit
    }
}
function New-PublishModule {
    [cmdletbinding()]
    param($projectName,
        $apikey,
        [bool] $RequireForce)
    Publish-Module -Name $projectName -Repository PSGallery -NuGetApiKey $apikey -Force:$RequireForce -verbose
}
<#
.SYNOPSIS
  Outputs to a UTF-8-encoded file *without a BOM* (byte-order mark).
 
.DESCRIPTION
  Mimics the most important aspects of Out-File:
  * Input objects are sent to Out-String first.
  * -Append allows you to append to an existing file, -NoClobber prevents
    overwriting of an existing file.
  * -Width allows you to specify the line width for the text representations
     of input objects that aren't strings.
  However, it is not a complete implementation of all Out-String parameters:
  * Only a literal output path is supported, and only as a parameter.
  * -Force is not supported.
 
  Caveat: *All* pipeline input is buffered before writing output starts,
          but the string representations are generated and written to the target
          file one by one.
 
.NOTES
  The raison d'être for this advanced function is that, as of PowerShell v5,
  Out-File still lacks the ability to write UTF-8 files without a BOM:
  using -Encoding UTF8 invariably prepends a BOM.
 
#>

function Out-FileUtf8NoBom {
    [CmdletBinding()]
    param([Parameter(Mandatory, Position = 0)] [string] $LiteralPath,
        [switch] $Append,
        [switch] $NoClobber,
        [AllowNull()] [int] $Width,
        [Parameter(ValueFromPipeline)] $InputObject)
    [System.IO.Directory]::SetCurrentDirectory($PWD)
    $LiteralPath = [IO.Path]::GetFullPath($LiteralPath)
    if ($NoClobber -and (Test-Path $LiteralPath)) { Throw [IO.IOException] "The file '$LiteralPath' already exists." }
    $sw = New-Object IO.StreamWriter $LiteralPath, $Append
    $htOutStringArgs = @{ }
    if ($Width) { $htOutStringArgs += @{Width = $Width } }
    try { $Input | Out-String -Stream @htOutStringArgs | ForEach-Object { $sw.WriteLine($_) } } finally { $sw.Dispose() }
}
function Remove-Directory {
    [CmdletBinding()]
    param ([string] $Directory)
    if ($Directory) {
        $exists = Test-Path -Path $Directory
        if ($exists) { Remove-Item -Path $Directory -Confirm:$false -Recurse } else { }
    }
}
$Script:FormatterSettings = @{IncludeRules = @('PSPlaceOpenBrace',
        'PSPlaceCloseBrace',
        'PSUseConsistentWhitespace',
        'PSUseConsistentIndentation',
        'PSAlignAssignmentStatement',
        'PSUseCorrectCasing')
    Rules                                  = @{PSPlaceOpenBrace = @{Enable = $true
            OnSameLine                                                     = $true
            NewLineAfter                                                   = $true
            IgnoreOneLineBlock                                             = $true
        }
        PSPlaceCloseBrace                                       = @{Enable = $true
            NewLineAfter                      = $false
            IgnoreOneLineBlock                = $true
            NoEmptyLineBefore                 = $false
        }
        PSUseConsistentIndentation                              = @{Enable = $true
            Kind                              = 'space'
            PipelineIndentation               = 'IncreaseIndentationAfterEveryPipeline'
            IndentationSize                   = 4
        }
        PSUseConsistentWhitespace                               = @{Enable = $true
            CheckInnerBrace                   = $true
            CheckOpenBrace                    = $true
            CheckOpenParen                    = $true
            CheckOperator                     = $true
            CheckPipe                         = $true
            CheckSeparator                    = $true
        }
        PSAlignAssignmentStatement                              = @{Enable = $true
            CheckHashtable                    = $true
        }
        PSUseCorrectCasing                                      = @{Enable = $true }
    }
}
function Set-LinkedFiles {
    [CmdletBinding()]
    param([string[]] $LinkFiles,
        [string] $FullModulePath,
        [string] $FullProjectPath,
        [switch] $Delete)
    foreach ($file in $LinkFiles) {
        [string] $Path = "$FullModulePath\$file"
        [string] $Path2 = "$FullProjectPath\$file"
        if ($Delete) { if (Test-ReparsePoint -path $Path) { Remove-Item $Path -Confirm:$false } }
        $null = cmd /c mklink $path $path2
    }
}
function Start-ModuleBuilding {
    [CmdletBinding()]
    param([System.Collections.IDictionary] $Configuration)
    $DestinationPaths = @{ }
    if ($Configuration.Information.Manifest.CompatiblePSEditions) {
        if ($Configuration.Information.Manifest.CompatiblePSEditions -contains 'Desktop') { $DestinationPaths.Desktop = [IO.path]::Combine($Configuration.Information.DirectoryModules, $Configuration.Information.ModuleName) }
        if ($Configuration.Information.Manifest.CompatiblePSEditions -contains 'Core') { $DestinationPaths.Core = [IO.path]::Combine($Configuration.Information.DirectoryModulesCore, $Configuration.Information.ModuleName) }
    }
    [string] $Random = Get-Random 10000000000
    [string] $FullModulePath = [IO.path]::GetTempPath() + '' + $Configuration.Information.ModuleName + "_TEMP_$Random"
    [string] $FullTemporaryPath = [IO.path]::GetTempPath() + '' + $Configuration.Information.ModuleName
    [string] $FullProjectPath = [IO.Path]::Combine($Configuration.Information.DirectoryProjects, $Configuration.Information.ModuleName)
    [string] $ProjectName = $Configuration.Information.ModuleName
    Write-Text '----------------------------------------------------'
    Write-Text "[i] Project Name: $ProjectName" -Color Yellow
    Write-Text "[i] Full module temporary path: $FullModulePath" -Color Yellow
    Write-Text "[i] Full project path: $FullProjectPath" -Color Yellow
    Write-Text "[i] Full temporary path: $FullTemporaryPath" -Color Yellow
    Write-Text "[i] PSScriptRoot: $PSScriptRoot" -Color Yellow
    Write-Text "[i] Current PSEdition: $PSEdition" -Color Yellow
    Write-Text "[i] Destination Desktop: $($DestinationPaths.Desktop)" -Color Yellow
    Write-Text "[i] Destination Core: $($DestinationPaths.Desktop)" -Color Yellow
    Write-Text '----------------------------------------------------'
    $CurrentLocation = (Get-Location).Path
    Set-Location -Path $FullProjectPath
    Remove-Directory $FullModulePath
    Remove-Directory $FullTemporaryPath
    Add-Directory $FullModulePath
    Add-Directory $FullTemporaryPath
    $LinkDirectories = @()
    $LinkPrivatePublicFiles = @()
    $Configuration.Information.Manifest.RootModule = "$($ProjectName).psm1"
    $Configuration.Information.Manifest.FunctionsToExport = @()
    $Configuration.Information.Manifest.CmdletsToExport = @()
    $Configuration.Information.Manifest.VariablesToExport = @()
    $Configuration.Information.Manifest.AliasesToExport = @()
    $Exclude = '.*', 'Ignore', 'Examples', 'package.json', 'Publish', 'Docs'
    if ($Configuration.Steps.BuildModule) {
        $PreparingFilesTime = Write-Text "[+] Preparing files and folders" -Start
        if ($PSEdition -eq 'core') {
            $Directories = @($TempDirectories = Get-ChildItem -Path $FullProjectPath -Directory -Exclude $Exclude -FollowSymlink
                @($TempDirectories
                    $TempDirectories | Get-ChildItem -Directory -Recurse -FollowSymlink))
            $Files = Get-ChildItem -Path $FullProjectPath -Exclude $Exclude -FollowSymlink | Get-ChildItem -File -Recurse -FollowSymlink
            $FilesRoot = Get-ChildItem -Path "$FullProjectPath\*" -Include '*.psm1', '*.psd1', 'License*' -File -FollowSymlink
        } else {
            $Directories = @($TempDirectories = Get-ChildItem -Path $FullProjectPath -Directory -Exclude $Exclude
                @($TempDirectories
                    $TempDirectories | Get-ChildItem -Directory -Recurse))
            $Files = Get-ChildItem -Path $FullProjectPath -Exclude '.*', 'Ignore', 'Examples', 'package.json', 'Publish', 'Docs' | Get-ChildItem -File -Recurse
            $FilesRoot = Get-ChildItem -Path "$FullProjectPath\*" -Include '*.psm1', '*.psd1', 'License*' -File
        }
        $LinkDirectories = @(foreach ($directory in $Directories) {
                $RelativeDirectoryPath = (Resolve-Path -LiteralPath $directory.FullName -Relative).Replace('.\', '')
                $RelativeDirectoryPath = "$RelativeDirectoryPath\"
                $RelativeDirectoryPath
            })
        $AllFiles = foreach ($File in $Files) {
            $RelativeFilePath = (Resolve-Path -LiteralPath $File.FullName -Relative).Replace('.\', '')
            $RelativeFilePath
        }
        $RootFiles = foreach ($File in $FilesRoot) {
            $RelativeFilePath = (Resolve-Path -LiteralPath $File.FullName -Relative).Replace('.\', '')
            $RelativeFilePath
        }
        $LinkFilesRoot = @(foreach ($File in $RootFiles | Sort-Object -Unique) {
                switch -Wildcard ($file) {
                    '*.psd1' { $File }
                    '*.psm1' { $File }
                    'License*' { $File }
                }
            })
        $LinkPrivatePublicFiles = @(foreach ($file in $AllFiles | Sort-Object -Unique) {
                switch -Wildcard ($file) {
                    '*.ps1' {
                        Add-FilesWithFolders -file $file -FullProjectPath $FullProjectPath -directory 'Private', 'Public', 'Enums'
                        continue
                    }
                    '*.*' {
                        Add-FilesWithFolders -file $file -FullProjectPath $FullProjectPath -directory 'Images\', 'Resources\', 'Templates\', 'Bin\', 'Lib\'
                        continue
                    }
                }
            })
        $LinkPrivatePublicFiles = $LinkPrivatePublicFiles | Select-Object -Unique
        Write-Text -End -Time $PreparingFilesTime
        $AliasesAndFunctions = Write-TextWithTime -Text '[+] Preparing function and aliases names' { Get-FunctionAliasesFromFolder -FullProjectPath $FullProjectPath -Files $Files }
        if ($AliasesAndFunctions.Function) { $Configuration.Information.Manifest.FunctionsToExport = $AliasesAndFunctions.Function }
        if ($AliasesAndFunctions.Alias) { $Configuration.Information.Manifest.AliasesToExport = $AliasesAndFunctions.Alias }
        if (-not [string]::IsNullOrWhiteSpace($Configuration.Information.ScriptsToProcess)) {
            $StartsWithEnums = "$($Configuration.Information.ScriptsToProcess)\"
            $FilesEnums = @($LinkPrivatePublicFiles | Where-Object { ($_).StartsWith($StartsWithEnums) })
            if ($FilesEnums.Count -gt 0) {
                Write-TextWithTime -Text "[+] ScriptsToProcess export $FilesEnums"
                $Configuration.Information.Manifest.ScriptsToProcess = $FilesEnums
            }
        }
        $PSD1FilePath = "$FullProjectPath\$ProjectName.psd1"
        New-PersonalManifest -Configuration $Configuration -ManifestPath $PSD1FilePath -AddScriptsToProcess
        Format-Code -FilePath $PSD1FilePath -FormatCode $Configuration.Options.Standard.FormatCodePSD1
    }
    if ($Configuration.Steps.BuildModule.Merge) {
        foreach ($Directory in $LinkDirectories) {
            $Dir = "$FullTemporaryPath\$Directory"
            Add-Directory $Dir
        }
        $LinkDirectoriesWithSupportFiles = $LinkDirectories | Where-Object { $_ -ne 'Public\' -and $_ -ne 'Private\' }
        foreach ($Directory in $LinkDirectoriesWithSupportFiles) {
            $Dir = "$FullModulePath\$Directory"
            Add-Directory $Dir
        }
        $LinkingFilesTime = Write-Text "[+] Linking files from root and sub directories" -Start
        Set-LinkedFiles -LinkFiles $LinkFilesRoot -FullModulePath $FullTemporaryPath -FullProjectPath $FullProjectPath
        Set-LinkedFiles -LinkFiles $LinkPrivatePublicFiles -FullModulePath $FullTemporaryPath -FullProjectPath $FullProjectPath
        Write-Text -End -Time $LinkingFilesTime
        if (-not [string]::IsNullOrWhiteSpace($Configuration.Information.LibrariesCore)) {
            $StartsWithCore = "$($Configuration.Information.LibrariesCore)\"
            $FilesLibrariesCore = $LinkPrivatePublicFiles | Where-Object { ($_).StartsWith($StartsWithCore) }
        }
        if (-not [string]::IsNullOrWhiteSpace($Configuration.Information.LibrariesDefault)) {
            $StartsWithDefault = "$($Configuration.Information.LibrariesDefault)\"
            $FilesLibrariesDefault = $LinkPrivatePublicFiles | Where-Object { ($_).StartsWith($StartsWithDefault) }
        }
        Merge-Module -ModuleName $ProjectName -ModulePathSource $FullTemporaryPath -ModulePathTarget $FullModulePath -Sort $Configuration.Options.Merge.Sort -FunctionsToExport $Configuration.Information.Manifest.FunctionsToExport -AliasesToExport $Configuration.Information.Manifest.AliasesToExport -LibrariesCore $FilesLibrariesCore -LibrariesDefault $FilesLibrariesDefault -FormatCodePSM1 $Configuration.Options.Merge.FormatCodePSM1 -FormatCodePSD1 $Configuration.Options.Merge.FormatCodePSD1 -Configuration $Configuration
    } else {
        foreach ($Directory in $LinkDirectories) {
            $Dir = "$FullModulePath\$Directory"
            Add-Directory $Dir
        }
        $LinkingFilesTime = Write-Text "[+] Linking files from root and sub directories" -Start
        Set-LinkedFiles -LinkFiles $LinkFilesRoot -FullModulePath $FullModulePath -FullProjectPath $FullProjectPath
        Set-LinkedFiles -LinkFiles $LinkPrivatePublicFiles -FullModulePath $FullModulePath -FullProjectPath $FullProjectPath
        Write-Text -End -Time $LinkingFilesTime
    }
    Set-Location -Path $CurrentLocation
    if ($DestinationPaths.Desktop) {
        Write-TextWithTime -Text "[+] Copy module to PowerShell 5 destination: $($DestinationPaths.Desktop)" { Remove-Directory -Directory $DestinationPaths.Desktop
            Add-Directory -Directory $DestinationPaths.Desktop
            Get-ChildItem -LiteralPath $FullModulePath | Copy-Item -Destination $DestinationPaths.Desktop -Recurse }
    }
    if ($DestinationPaths.Core) {
        Write-TextWithTime -Text "[+] Copy module to PowerShell 6/7 destination: $($DestinationPaths.Core)" { Remove-Directory -Directory $DestinationPaths.Core
            Add-Directory -Directory $DestinationPaths.Core
            Get-ChildItem -LiteralPath $FullModulePath | Copy-Item -Destination $DestinationPaths.Core -Recurse }
    }
    if ($Configuration.Steps.BuildModule.Releases) {
        $TagName = "v$($Configuration.Information.Manifest.ModuleVersion)"
        $FileName = -join ("$TagName", '.zip')
        $FolderPathReleases = [System.IO.Path]::Combine($FullProjectPath, 'Releases')
        $ZipPath = [System.IO.Path]::Combine($FullProjectPath, 'Releases', $FileName)
        Write-TextWithTime -Text "[+] Compressing final merged release $ZipPath" { $null = New-Item -ItemType Directory -Path $FolderPathReleases -Force
            if ($DestinationPaths.Desktop) {
                $CompressPath = [System.IO.Path]::Combine($DestinationPaths.Desktop, '*')
                Compress-Archive -Path $CompressPath -DestinationPath $ZipPath -Force
            }
            if ($DestinationPaths.Core -and -not $DestinationPaths.Desktop) {
                $CompressPath = [System.IO.Path]::Combine($DestinationPaths.Core, '*')
                Compress-Archive -Path $CompressPath -DestinationPath $ZipPath -Force
            } }
        if ($Configuration.Steps.PublishModule.GitHub) {
            if ($Configuration.Options.GitHub.FromFile) { $GitHubAccessToken = Get-Content -LiteralPath $Configuration.Options.GitHub.ApiKey } else { $GitHubAccessToken = $Configuration.Options.GitHub.ApiKey }
            if ($GitHubAccessToken) {
                if ($Configuration.Options.GitHub.RepositoryName) { $GitHubRepositoryName = $Configuration.Options.GitHub.RepositoryName } else { $GitHubRepositoryName = $ProjectName }
                if (Test-Path -LiteralPath $ZipPath) {
                    if ($Configuration.Steps.PublishModule.Prerelease -ne '') { $IsPreRelease = $true } else { $IsPreRelease = $false }
                    $StatusGithub = New-GitHubRelease -GitHubUsername $Configuration.Options.GitHub.UserName -GitHubRepositoryName $GitHubRepositoryName -GitHubAccessToken $GitHubAccessToken -TagName $TagName -AssetFilePaths $ZipPath -IsPreRelease $IsPreRelease
                    if ($StatusGithub.ReleaseCreationSucceeded -and $statusGithub.Succeeded) {
                        $GithubColor = 'Green'
                        $GitHubText = '+'
                    } else {
                        $GithubColor = 'Red'
                        $GitHubText = '-'
                    }
                    Write-Text "[$GitHubText] GitHub Release Creation Status: $($StatusGithub.ReleaseCreationSucceeded)" -Color $GithubColor
                    Write-Text "[$GitHubText] GitHub Release Succeeded: $($statusGithub.Succeeded)" -Color $GithubColor
                    Write-Text "[$GitHubText] GitHub Release Asset Upload Succeeded: $($statusGithub.AllAssetUploadsSucceeded)" -Color $GithubColor
                    Write-Text "[$GitHubText] GitHub Release URL: $($statusGitHub.ReleaseUrl)" -Color $GithubColor
                    if ($statusGithub.ErrorMessage) { Write-Text "[$GitHubText] GitHub Release ErrorMessage: $($statusGithub.ErrorMessage)" -Color $GithubColor }
                }
            }
        }
    }
    if ($Configuration.Steps.PublishModule.Enabled) {
        if ($Configuration.Options.PowerShellGallery.FromFile) {
            $ApiKey = Get-Content -Path $Configuration.Options.PowerShellGallery.ApiKey
            New-PublishModule -ProjectName $Configuration.Information.ModuleName -ApiKey $ApiKey -RequireForce $Configuration.Steps.PublishModule.RequireForce
        } else { New-PublishModule -ProjectName $Configuration.Information.ModuleName -ApiKey $Configuration.Options.PowerShellGallery.ApiKey -RequireForce $Configuration.Steps.PublishModule.RequireForce }
    }
    if ($Configuration) {
        $TemporaryVerbosePreference = $VerbosePreference
        $VerbosePreference = $false
        if ($Configuration.Options.ImportModules.RequiredModules) { Write-TextWithTime -Text '[+] Importing modules - REQUIRED' { foreach ($Module in $Configuration.Information.Manifest.RequiredModules) { Import-Module -Name $Module -Force -ErrorAction Stop -Verbose:$false } } }
        if ($Configuration.Options.ImportModules.Self) { Write-TextWithTime -Text '[+] Importing module - SELF' { Import-Module -Name $ProjectName -Force -ErrorAction Stop -Verbose:$false } }
        $VerbosePreference = $TemporaryVerbosePreference
        if ($Configuration.Steps.BuildDocumentation) {
            $WarningVariablesMarkdown = @()
            $DocumentationPath = "$FullProjectPath\$($Configuration.Options.Documentation.Path)"
            $ReadMePath = "$FullProjectPath\$($Configuration.Options.Documentation.PathReadme)"
            Write-Text "[+] Generating documentation to $DocumentationPath with $ReadMePath" -Color Yellow
            if (-not (Test-Path -Path $DocumentationPath)) { $null = New-Item -Path "$FullProjectPath\Docs" -ItemType Directory -Force }
            $Files = Get-ChildItem -Path $DocumentationPath
            if ($Files.Count -gt 0) { $null = Update-MarkdownHelpModule $DocumentationPath -RefreshModulePage -ModulePagePath $ReadMePath -ErrorAction Stop -WarningVariable +WarningVariablesMarkdown -WarningAction SilentlyContinue } else {
                $null = New-MarkdownHelp -Module $ProjectName -WithModulePage -OutputFolder $DocumentationPath -ErrorAction Stop -WarningVariable +WarningVariablesMarkdown -WarningAction SilentlyContinue
                $null = Move-Item -Path "$DocumentationPath\$ProjectName.md" -Destination $ReadMePath
                if ($Configuration.Options.Documentation.UpdateWhenNew) { $null = Update-MarkdownHelpModule $DocumentationPath -RefreshModulePage -ModulePagePath $ReadMePath -ErrorAction Stop -WarningVariable +WarningVariablesMarkdown -WarningAction SilentlyContinue }
            }
            foreach ($_ in $WarningVariablesMarkdown) { Write-Text "[-] Documentation warning: $_" -Color Yellow }
        }
    }
    Write-Text "[+] Cleaning up directories created in TEMP directory" -Color Yellow
    Remove-Directory $FullModulePath
    Remove-Directory $FullTemporaryPath
}
function Test-ReparsePoint {
    [CmdletBinding()]
    param ([string]$path)
    $file = Get-Item $path -Force -ea SilentlyContinue
    return [bool]($file.Attributes -band [IO.FileAttributes]::ReparsePoint)
}
function Write-PowerShellHashtable {
    [cmdletbinding()]
    <#
    .Synopsis
        Takes an creates a script to recreate a hashtable
    .Description
        Allows you to take a hashtable and create a hashtable you would embed into a script.
 
        Handles nested hashtables and indents nested hashtables automatically.
    .Parameter inputObject
        The hashtable to turn into a script
    .Parameter scriptBlock
        Determines if a string or a scriptblock is returned
    .Example
        # Corrects the presentation of a PowerShell hashtable
        @{Foo='Bar';Baz='Bing';Boo=@{Bam='Blang'}} | Write-PowerShellHashtable
    .Outputs
        [string]
    .Outputs
        [ScriptBlock]
    .Link
        https://github.com/StartAutomating/Pipeworks
        about_hash_tables
    #>

    [OutputType([string], [ScriptBlock])]
    param([Parameter(Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [PSObject]
        $InputObject,
        [Alias('ScriptBlock')]
        [switch]$AsScriptBlock,
        [Switch]$Sort)
    process {
        $callstack = @(foreach ($_ in (Get-PSCallStack)) { if ($_.Command -eq "Write-PowerShellHashtable") { $_ } })
        $depth = $callStack.Count
        if ($inputObject -isnot [Hashtable]) {
            $newInputObject = @{PSTypeName = @($inputobject.pstypenames)[-1] }
            foreach ($prop in $inputObject.psobject.properties) { $newInputObject[$prop.Name] = $prop.Value }
            $inputObject = $newInputObject
        }
        if ($inputObject -is [Hashtable]) {
            $scriptString = ""
            $indent = $depth * 4
            $scriptString += "@{
"

            $items = $inputObject.GetEnumerator()
            if ($Sort) { $items = $items | Sort-Object Key }
            foreach ($kv in $items) {
                $scriptString += " " * $indent
                $keyString = "$($kv.Key)"
                if ($keyString.IndexOfAny(" _.#-+:;()'!?^@#$%&".ToCharArray()) -ne -1) { if ($keyString.IndexOf("'") -ne -1) { $scriptString += "'$($keyString.Replace("'","''"))'=" } else { $scriptString += "'$keyString'=" } } elseif ($keyString) { $scriptString += "$keyString=" }
                $value = $kv.Value
                if ($value -is [string]) { $value = "'" + $value.Replace("'", "''").Replace("’", "’’").Replace("‘", "‘‘") + "'" } elseif ($value -is [ScriptBlock]) { $value = "{$value}" } elseif ($value -is [switch]) { $value = if ($value) { '$true' } else { '$false' } } elseif ($value -is [DateTime]) { $value = if ($value) { "[DateTime]'$($value.ToString("o"))'" } } elseif ($value -is [bool]) { $value = if ($value) { '$true' } else { '$false' } } elseif ($value -and $value.GetType -and ($value.GetType().IsArray -or $value -is [Collections.IList])) {
                    $value = foreach ($v in $value) { if ($v -is [Hashtable]) { Write-PowerShellHashtable $v } elseif ($v -is [Object] -and $v -isnot [string]) { Write-PowerShellHashtable $v } else { ("'" + "$v".Replace("'", "''").Replace("’", "’’").Replace("‘", "‘‘") + "'") } }
                    $oldOfs = $ofs
                    $ofs = ",$(' ' * ($indent + 4))"
                    $value = "$value"
                    $ofs = $oldOfs
                } elseif ($value -as [Hashtable[]]) {
                    $value = foreach ($v in $value) { Write-PowerShellHashtable $v }
                    $value = $value -join ","
                } elseif ($value -is [Hashtable]) { $value = "$(Write-PowerShellHashtable $value)" } elseif ($value -as [Double]) { $value = "$value" } else {
                    $valueString = "'$value'"
                    if ($valueString[0] -eq "'" -and
                        $valueString[1] -eq "@" -and
                        $valueString[2] -eq "{") { $value = Write-PowerShellHashtable -InputObject $value } else { $value = $valueString }
                }
                $scriptString += "$value
"

            }
            $scriptString += " " * ($depth - 1) * 4
            $scriptString += "}"
            if ($AsScriptBlock) { [ScriptBlock]::Create($scriptString) } else { $scriptString }
        }
    }
}
function Write-Text {
    [CmdletBinding()]
    param([string] $Text,
        [System.ConsoleColor] $Color = [System.ConsoleColor]::Cyan,
        [System.ConsoleColor] $ColorTime = [System.ConsoleColor]::Green,
        [switch] $Start,
        [switch] $End,
        [System.Diagnostics.Stopwatch] $Time)
    if (-not $Start -and -not $End) { Write-Host "$Text" -ForegroundColor $Color }
    if ($Start) {
        Write-Host "$Text" -NoNewline -ForegroundColor $Color
        $Time = [System.Diagnostics.Stopwatch]::StartNew()
    }
    if ($End) {
        $TimeToExecute = $Time.Elapsed.ToString()
        Write-Host " [Time: $TimeToExecute]" -ForegroundColor $ColorTime
        $Time.Stop()
    } else { if ($Time) { return $Time } }
}
function Write-TextWithTime {
    [CmdletBinding()]
    param([ScriptBlock] $Content,
        [string] $Text,
        [switch] $Continue,
        [System.ConsoleColor] $Color = [System.ConsoleColor]::Cyan,
        [System.ConsoleColor] $ColorTime = [System.ConsoleColor]::Green)
    Write-Host "$Text" -NoNewline -ForegroundColor $Color
    $Time = [System.Diagnostics.Stopwatch]::StartNew()
    if ($null -ne $Content) { & $Content }
    $TimeToExecute = $Time.Elapsed.ToString()
    Write-Host " [Time: $TimeToExecute]" -ForegroundColor $ColorTime
    if (-not $Continue) { $Time.Stop() }
}
function Get-GitLog {
    [CmdLetBinding(DefaultParameterSetName = 'Default')]
    param ([Parameter(ParameterSetName = 'Default', Mandatory)]
        [Parameter(ParameterSetName = 'SourceTarget', Mandatory)]
        [ValidateScript( { Resolve-Path -Path $_ | Test-Path })]
        [string]$GitFolder,
        [Parameter(ParameterSetName = 'SourceTarget', Mandatory)]
        [string]$StartCommitId,
        [Parameter(ParameterSetName = 'SourceTarget')]
        [string]$EndCommitId = 'HEAD')
    Push-Location
    try {
        Set-Location -Path $GitFolder
        $GitCommand = Get-Command -Name git -ErrorAction Stop
    } catch { $PSCmdlet.ThrowTerminatingError($_) }
    if ($StartCommitId) { $GitLogCommand = '"{0}" log --oneline --format="%H`t%h`t%ai`t%an`t%ae`t%ci`t%cn`t%ce`t%s`t%f" {1}...{2} 2>&1' -f $GitCommand.Source, $StartCommitId, $EndCommitId } else { $GitLogCommand = '"{0}" log --oneline --format="%H`t%h`t%ai`t%an`t%ae`t%ci`t%cn`t%ce`t%s`t%f" 2>&1' -f $GitCommand.Source }
    Write-Verbose -Message $GitLogCommand
    $GitLog = Invoke-Expression -Command "& $GitLogCommand" -ErrorAction SilentlyContinue
    Pop-Location
    if ($GitLog[0] -notmatch 'fatal:') { $GitLog | ConvertFrom-Csv -Delimiter "`t" -Header 'CommitId', 'ShortCommitId', 'AuthorDate', 'AuthorName', 'AuthorEmail', 'CommitterDate', 'CommitterName', 'ComitterEmail', 'CommitMessage', 'SafeCommitMessage' } else { if ($GitLog[0] -like "fatal: ambiguous argument '*...*'*") { Write-Warning -Message 'Unknown revision. Please check the values for StartCommitId or EndCommitId; omit the parameters to retrieve the entire log.' } else { Write-Error -Category InvalidArgument -Message ($GitLog -join "`n") } }
}
function Get-MissingFunctions {
    [CmdletBinding()]
    param([alias('Path')][string] $FilePath,
        [string[]] $Functions,
        [switch] $Summary,
        [switch] $SummaryWithCommands)
    $ListCommands = [System.Collections.Generic.List[Object]]::new()
    $Result = Get-ScriptCommands -FilePath $FilePath -CommandsOnly
    $FilteredCommands = Get-FilteredScriptCommands -Commands $Result -NotUnknown -NotCmdlet -Functions $Functions -NotApplication
    foreach ($_ in $FilteredCommands) { $ListCommands.Add($_) }
    Get-RecursiveCommands -Commands $FilteredCommands
    $FunctionsOutput = foreach ($_ in $ListCommands) { "function $($_.Name) { $($_.ScriptBlock) }" }
    if ($SummaryWithCommands) {
        $Hash = @{Summary = $ListCommands
            Functions     = $FunctionsOutput
        }
        return $Hash
    } elseif ($Summary) { return $ListCommands } else { return $FunctionsOutput }
}
function Get-ScriptCommands {
    [cmdletBinding(DefaultParameterSetName = 'File')]
    param ([alias('Path')][Parameter(ParameterSetName = 'File')][string] $FilePath,
        [alias('ScriptBlock')][scriptblock] $Code,
        [switch] $CommandsOnly)
    begin { $Errors = $null }
    process {
        $Errors = $null
        if ($Code) { $CodeRead = $Code } else { $CodeRead = Get-Content -Path $FilePath -Raw -Encoding Default }
        $Tokens = [System.Management.Automation.PSParser]::Tokenize($CodeRead, [ref]$Errors)
        $Commands = foreach ($_ in $Tokens) { if ($_.Type -eq 'Command') { $_ } }
        if ($CommandsOnly) { $Commands.Content | Sort-Object -Unique } else { $Commands }
    }
}
function New-PrepareModule {
    [alias('New-BuildModule')]
    [CmdletBinding()]
    param ([System.Collections.IDictionary] $Configuration)
    if (-not $Configuration) { return }
    $GlobalTime = [System.Diagnostics.Stopwatch]::StartNew()
    if (-not $Configuration.Information.DirectoryModulesCore) { $Configuration.Information.DirectoryModulesCore = "$Env:USERPROFILE\Documents\PowerShell\Modules" }
    if (-not $Configuration.Information.DirectoryModules) { $Configuration.Information.DirectoryModules = "$Env:USERPROFILE\Documents\WindowsPowerShell\Modules" }
    if ($Configuration.Steps.BuildModule.Enable -or $Configuration.Steps.BuildModule.EnableDesktop -or $Configuration.Steps.BuildModule.EnableCore) { Start-ModuleBuilding -Configuration $Configuration }
    $Execute = "$($GlobalTime.Elapsed.Days) days, $($GlobalTime.Elapsed.Hours) hours, $($GlobalTime.Elapsed.Minutes) minutes, $($GlobalTime.Elapsed.Seconds) seconds, $($GlobalTime.Elapsed.Milliseconds) milliseconds"
    Write-Host "[i] Module Building " -NoNewline -ForegroundColor Yellow
    Write-Host "[Time Total: $Execute]" -ForegroundColor Green
}
function Remove-Comments {
    Param ([string] $FilePath,
        [parameter(ValueFromPipeline = $True)] $Scriptblock,
        [string] $ScriptContent)
    if ($PSBoundParameters['FilePath']) {
        $ScriptBlockString = [IO.File]::ReadAllText((Resolve-Path $FilePath))
        $ScriptBlock = [ScriptBlock]::Create($ScriptBlockString)
    } elseif ($PSBoundParameters['ScriptContent']) { $ScriptBlock = [ScriptBlock]::Create($ScriptContent) } else { }
    $OldScript = $ScriptBlock -join [environment]::NewLine
    If (-not $OldScript.Trim(" `n`r`t")) { return }
    $Tokens = [System.Management.Automation.PSParser]::Tokenize($OldScript, [ref]$Null)
    $AllowedComments = @('requires'
        '.SYNOPSIS'
        '.DESCRIPTION'
        '.PARAMETER'
        '.EXAMPLE'
        '.INPUTS'
        '.OUTPUTS'
        '.NOTES'
        '.LINK'
        '.COMPONENT'
        '.ROLE'
        '.FUNCTIONALITY'
        '.FORWARDHELPCATEGORY'
        '.REMOTEHELPRUNSPACE'
        '.EXTERNALHELP')
    $Tokens = $Tokens.ForEach{ If ($_.Type -ne 'Comment') { $_ } Else {
            $CommentText = $_.Content.Substring($_.Content.IndexOf('#') + 1)
            $FirstInnerToken = [System.Management.Automation.PSParser]::Tokenize($CommentText, [ref]$Null) |
                Where-Object { $_.Type -ne 'NewLine' } |
                    Select-Object -First 1
            If ($FirstInnerToken.Content -in $AllowedComments) { $_ }
        } }
    $SkipNext = $False
    $ScriptProcessing = @(If ($Tokens.Count -gt 1) {
            ForEach ($i in (0..($Tokens.Count - 2))) {
                If (-not $SkipNext -and
                    $Tokens[$i ].Type -ne 'LineContinuation' -and ($Tokens[$i ].Type -notin ('NewLine', 'StatementSeparator') -or
                        $Tokens[$i + 1].Type -notin ('NewLine', 'StatementSeparator', 'GroupEnd'))) {
                    If ($Tokens[$i].Type -in ('String', 'Variable')) { $OldScript.Substring($Tokens[$i].Start, $Tokens[$i].Length) } Else { $Tokens[$i].Content }
                    If ($Tokens[$i ].Type -notin ('NewLine', 'GroupStart', 'StatementSeparator') -and
                        $Tokens[$i + 1].Type -notin ('NewLine', 'GroupEnd', 'StatementSeparator') -and
                        $Tokens[$i].EndLine -eq $Tokens[$i + 1].StartLine -and
                        $Tokens[$i + 1].StartColumn - $Tokens[$i].EndColumn -gt 0) { ' ' }
                    $SkipNext = $Tokens[$i].Type -eq 'GroupStart' -and $Tokens[$i + 1].Type -in ('NewLine', 'StatementSeparator')
                } Else { $SkipNext = $SkipNext -and $Tokens[$i + 1].Type -in ('NewLine', 'StatementSeparator') }
            }
        }
        If ($Tokens) { If ($Tokens[$i].Type -in ('String', 'Variable')) { $OldScript.Substring($Tokens[-1].Start, $Tokens[-1].Length) } Else { $Tokens[-1].Content } })
    [string] $NewScriptText = $ScriptProcessing -join ''
    $NewScriptText = $NewScriptText.TrimStart("`n`r;")
    If ($Scriptblock.Count -eq 1) { If ($Scriptblock[0] -is [scriptblock]) { return [scriptblock]::Create($NewScriptText) } Else { return $NewScriptText } } Else { return $NewScriptText.Split("`n`r", [System.StringSplitOptions]::RemoveEmptyEntries) }
}
Function Test-ScriptFile {
    <#
    .Synopsis
    Test a PowerShell script for cmdlets
    .Description
    This command will analyze a PowerShell script file and display a list of detected commands such as PowerShell cmdlets and functions. Commands will be compared to what is installed locally. It is recommended you run this on a Windows 8.1 client with the latest version of RSAT installed. Unknown commands could also be internally defined functions. If in doubt view the contents of the script file in the PowerShell ISE or a script editor.
    You can test any .ps1, .psm1 or .txt file.
    .Parameter Path
    The path to the PowerShell script file. You can test any .ps1, .psm1 or .txt file.
    .Example
    PS C:\> test-scriptfile C:\scripts\Remove-MyVM2.ps1
 
    CommandType Name ModuleName
    ----------- ---- ----------
        Cmdlet Disable-VMEventing Hyper-V
        Cmdlet ForEach-Object Microsoft.PowerShell.Core
        Cmdlet Get-VHD Hyper-V
        Cmdlet Get-VMSnapshot Hyper-V
        Cmdlet Invoke-Command Microsoft.PowerShell.Core
        Cmdlet New-PSSession Microsoft.PowerShell.Core
        Cmdlet Out-Null Microsoft.PowerShell.Core
        Cmdlet Out-String Microsoft.PowerShell.Utility
        Cmdlet Remove-Item Microsoft.PowerShell.Management
        Cmdlet Remove-PSSession Microsoft.PowerShell.Core
        Cmdlet Remove-VM Hyper-V
        Cmdlet Remove-VMSnapshot Hyper-V
        Cmdlet Write-Debug Microsoft.PowerShell.Utility
        Cmdlet Write-Verbose Microsoft.PowerShell.Utility
        Cmdlet Write-Warning Microsoft.PowerShell.Utility
 
    .Notes
    Original script provided by Jeff Hicks at (https://www.petri.com/powershell-problem-solver-find-script-commands)
    # https://twitter.com/donnie_taylor/status/1160920407031058432
    Test-ScriptFile -Path 'C:\Users\przemyslaw.klys\Documents\WindowsPowerShell\Modules\PSWinReportingV2\PSWinReportingV2.psm1' | Sort-Object -Property Source, Name | ft -AutoSize
    #>

    [cmdletbinding()]
    Param([Parameter(Position = 0, Mandatory = $True, HelpMessage = "Enter the path to a PowerShell script file,",
            ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
        [ValidatePattern("\.(ps1|psm1|txt)$")]
        [ValidateScript( { Test-Path $_ })]
        [string]$Path)
    Begin {
        Write-Verbose "Starting $($MyInvocation.Mycommand)"
        Write-Verbose "Defining AST variables"
        New-Variable astTokens -force
        New-Variable astErr -force
    }
    Process {
        Write-Verbose "Parsing $path"
        $null = [System.Management.Automation.Language.Parser]::ParseFile($Path, [ref]$astTokens, [ref]$astErr)
        $h = $astTokens | Group-Object tokenflags -AsHashTable -AsString
        $commandData = $h.CommandName | Where-Object { $_.text -notmatch "-TargetResource$" } |
            ForEach-Object { Write-Verbose "Processing $($_.text)"
                Try {
                    $cmd = $_.Text
                    $resolved = $cmd | Get-Command -ErrorAction Stop
                    if ($resolved.CommandType -eq 'Alias') {
                        Write-Verbose "Resolving an alias"
                        Write-Verbose "Detected the Where-Object alias '?'"
                        if ($cmd -eq '?') { Get-Command Where-Object } else {
                            $Resolved = $resolved.ResolvedCommandName | Get-Command
                            [PSCustomobject]@{CommandType = $resolved.CommandType
                                Name                      = $resolved.Name
                                ModuleName                = $resolved.ModuleName
                                Source                    = $resolved.Source
                            }
                        }
                    } else {
                        [PSCustomobject]@{CommandType = $resolved.CommandType
                            Name                      = $resolved.Name
                            ModuleName                = $resolved.ModuleName
                            Source                    = $resolved.Source
                        }
                    }
                } Catch {
                    Write-Verbose "Command is not recognized"
                    [PSCustomobject]@{CommandType = "Unknown"
                        Name                      = $cmd
                        ModuleName                = "Unknown"
                        Source                    = "Unknown"
                    }
                } }
    $CommandData
}
End { Write-Verbose -Message "Ending $($MyInvocation.Mycommand)" }
}
function Test-ScriptModule {
    [cmdletbinding()]
    param([string] $ModuleName,
        [ValidateSet('Name', 'CommandType', 'ModuleName', 'Source')] $SortName,
        [switch] $Unique)
    $Module = Get-Module -ListAvailable $ModuleName
    $Path = Join-Path -Path $Module.ModuleBase -ChildPath $Module.RootModule
    $Output = Test-ScriptFile -Path $Path
    if ($Unique) { $Output = $Output | Sort-Object -Property 'Name' -Unique:$Unique }
    if ($SortName) { $Output | Sort-Object -Property $SortName } else { $Output }
}
Export-ModuleMember -Function @('Get-GitLog', 'Get-MissingFunctions', 'Get-ScriptCommands', 'New-PrepareModule', 'Remove-Comments', 'Test-ScriptFile', 'Test-ScriptModule') -Alias @('New-BuildModule')