PSPublishModule.psm1
function Add-Directory { [CmdletBinding()] param( $dir ) $exists = Test-Path -Path $dir if ($exists -eq $false) { #Write-Color 'Creating directory ', $dir -Color White, Yellow $createdDirectory = mkdir $dir } else { #Write-Color 'Creating directory ', $dir, ' skipped.' -Color White, Yellow, Red } } function Add-FilesWithFolders { [CmdletBinding()] param ($file, $FullProjectPath, $directory) $LinkPrivatePublicFiles = @() #$path = $file.FullName.Replace("$FullProjectPath\", '') $path = $file foreach ($dir in $directory) { if ($path -like "$dir*") { $LinkPrivatePublicFiles += $path Write-Verbose "Adding file to linking list of files $path" # Write-Color 'Adding file to ', 'linking list', ' of files ', $path -Color White, Yellow, White, Yellow } } return $LinkPrivatePublicFiles } function Add-FilesWithFoldersNew { [CmdletBinding()] param($File, $FullProjectPath, $directory) <# $LinkPrivatePublicFiles = @() $path = $file.FullName.Replace("$FullProjectPath\", '') foreach ($dir in $directory) { if ($path.StartsWith($dir)) { $LinkPrivatePublicFiles += $path Write-Color 'Adding file to ', 'linking list', ' of files ', $path -Color White, Yellow, White, Yellow } } return $LinkPrivatePublicFiles #> } function Add-ObjectTo { [CmdletBinding()] param($Object, $Type) #Write-Color 'Adding ', $Object.Name, ' to ', $Type -Color White, Green, White, Yellow Write-Verbose "Adding $($Object) to $Type" return $Object } function Copy-File { [CmdletBinding()] param ( $Source, $Destination ) if ((Test-Path $Source) -and !(Test-Path $Destination)) { Copy-Item -Path $Source -Destination $Destination } } function Export-PSData { <# .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( # The data that will be exported [Parameter(Mandatory=$true,ValueFromPipeline=$true)] [PSObject[]] $InputObject, # The path to the data file [Parameter(Mandatory=$true,Position=0)] [string] $DataFile ) begin { $AllObjects = New-Object Collections.ArrayList } process { $null = $AllObjects.AddRange($InputObject) } end { #region Convert to Hashtables and export $text = $AllObjects | Write-PowerShellHashtable $text | Set-Content -Path $DataFile Get-Item -Path $DataFile #endregion Convert to Hashtables and export } } function Find-EnumsList { [CmdletBinding()] param ( [string] $ProjectPath ) $Enums = @( Get-ChildItem -Path $ProjectPath\Enums\*.ps1 -ErrorAction SilentlyContinue ) #Write-Verbose "Find-EnumsList - $ProjectPath\Enums" $Opening = '@(' $Closing = ')' $Adding = ',' $EnumsList = New-ArrayList Add-ToArray -List $EnumsList -Element $Opening Foreach ($import in @($Enums)) { $Entry = "'Enums\$($import.Name)'" Add-ToArray -List $EnumsList -Element $Entry Add-ToArray -List $EnumsList -Element $Adding } Remove-FromArray -List $EnumsList -LastElement Add-ToArray -List $EnumsList -Element $Closing return [string] $EnumsList } #using Namespace System.Management.Automation.Language Function Get-AliasTarget { [cmdletbinding()] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias('PSPath', 'FullName')] [string[]]$Path ) process { foreach ($File in $Path) { $FileAst = [System.Management.Automation.Language.Parser]::ParseFile($File, [ref]$null, [ref]$null) $FunctionName = $FileAst.FindAll( { param ($ast) $ast -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true).Name $AliasDefinitions = $FileAst.FindAll( { param ($ast) $ast -is [System.Management.Automation.Language.StringConstantExpressionAst] -And $ast.Value -match '(New|Set)-Alias' }, $true) $AliasTarget = $AliasDefinitions.Parent.CommandElements.Where( { $_.StringConstantType -eq 'BareWord' -and $_.Value -notin ('New-Alias', 'Set-Alias', $FunctionName) }).Value $Attributes = $FileAst.FindAll( { param ($ast) $ast -is [System.Management.Automation.Language.AttributeAst] }, $true) $AliasDefinitions = $Attributes.Where( {$_.TypeName.Name -eq 'Alias' -and $_.Parent -is [System.Management.Automation.Language.ParamBlockAst]}) $AliasTarget += $AliasDefinitions.PositionalArguments.Value [PsCustomObject]@{ Function = $FunctionName Alias = $AliasTarget } } } } #Get-AliasTarget -Path 'C:\Support\GitHub\PSSharedGoods\Public\Objects\Format-Stream.ps1' | Select-Object -ExpandProperty Alias #Get-FunctionAliases -Path 'C:\Support\GitHub\PSSharedGoods\Public\Objects\Format-Stream.ps1' #$Path = 'C:\Support\GitHub\PSSharedGoods\Public\Objects\Format-Stream.ps1' #Get-FunctionAliases -Path $path function Get-FunctionAliases { param( [string] $Path ) Import-Module $Path -Force -Verbose:$False $Names = Get-FunctionNames -Path $Path $Aliases = foreach ($Name in $Names) { Get-Alias | Where-Object {$_.Definition -eq $Name} } #$MyAliases = foreach ($Alias in $Aliases) { # if ($Alias -ne '') { # $Alias.Name # } #} return $Aliases } function Get-FunctionAliasesFromFolder { param( [string] $FullProjectPath, [string[]] $Folder ) foreach ($F in $Folder) { $Path = [IO.Path]::Combine($FullProjectPath, $F) $Files = Get-ChildItem -Path $Path -File -Recurse $AliasesToExport = foreach ($file in $Files) { #Get-FunctionAliases -Path $File.FullName Get-AliasTarget -Path $File.FullName | Select-Object -ExpandProperty Alias } $AliasesToExport } } function Get-FunctionNames { param( [string] $Path, [switch] $Recurse ) [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-FunctionNamesFromFolder { param( [string] $FullProjectPath, [string[]] $Folder ) foreach ($F in $Folder) { $Path = [IO.Path]::Combine($FullProjectPath, $F) $Files = Get-ChildItem -Path $Path -File -Recurse $FunctionToExport = foreach ($file in $Files) { Get-FunctionNames -Path $File.FullName } $FunctionToExport } } Function Get-ScriptComments { <# .Synopsis Get comments from a PowerShell script file. .Description This command will use the AST parser to go through a PowerShell script, either a .ps1 or .psm1 file, and display only the comments. .Example PS C:\> get-scriptcomments c:\scripts\MyScript.ps1 #> [cmdletbinding()] Param( [Parameter(Position = 0, Mandatory, HelpMessage = "Enter the path of a PS1 file", ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias("PSPath", "Name")] [ValidateScript( {Test-Path $_})] [ValidatePattern("\.ps(1|m1)$")] [string]$Path ) Begin { #Begin scriptblock Write-Verbose -Message "Starting $($MyInvocation.Mycommand)" #initialization commands #explicitly define some AST variables New-Variable astTokens -force New-Variable astErr -force } #close begin Process { #Process scriptblock #convert each path to a nice filesystem path $Path = Convert-Path -Path $Path Write-Verbose -Message "Parsing $Path" #Parse the file $ast = [System.Management.Automation.Language.Parser]::ParseFile($Path, [ref]$astTokens, [ref]$astErr) #filter tokens for comments and display text $asttokens.where( {$_.kind -eq 'comment'}) | Select-Object -ExpandProperty Text $ast } #close process End { #end scriptblock #ending the function Write-Verbose -Message "Ending $($MyInvocation.Mycommand)" } #close end } #close function 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 ) $ScriptFunctions = @( Get-ChildItem -Path $ModulePathSource\*.ps1 -ErrorAction SilentlyContinue -Recurse ) # $ModulePSM = @( Get-ChildItem -Path $ModulePathSource\*.psm1 -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 $Content = $Content.Replace('$PSScriptRoot\', '$PSScriptRoot\') $Content = $Content.Replace('$PSScriptRoot\', '$PSScriptRoot\') $Content | Add-Content $ModulePathTarget\$ModuleName.psm1 } New-PSMFile -Path $ModulePathTarget\$ModuleName.psm1 ` -FunctionNames $FunctionsToExport ` -FunctionAliaes $AliasesToExport ` -LibrariesCore $LibrariesCore ` -LibrariesDefault $LibrariesDefault <# foreach ($FilePath in $ScriptFunctions) { $Results = [System.Management.Automation.Language.Parser]::ParseFile($FilePath, [ref]$null, [ref]$null) $Functions = $Results.EndBlock.Extent.Text $Functions | Add-Content -Path "$ModulePathTarget\$ModuleName.psm1" } foreach ($FilePath in $ModulePSM) { $Content = Get-Content $FilePath $Content | Add-Content -Path "$ModulePathTarget\$ModuleName.psm1" } #> #Copy-Item -Path "$ModulePathSource\$ModuleName.psd1" "$ModulePathTarget\$ModuleName.psd1" New-PersonalManifest -Configuration $Configuration -ManifestPath "$ModulePathTarget\$ModuleName.psd1" } function New-CreateModule { [CmdletBinding()] param ( [string] $ProjectName, $ModulePath, $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-PersonalManifest { [CmdletBinding()] param( [System.Collections.IDictionary] $Configuration, [string] $ManifestPath, [switch] $AddScriptsToProcess ) $Manifest = $Configuration.Information.Manifest $Manifest.Path = $ManifestPath if (-not $AddScriptsToProcess) { $Manifest.ScriptsToProcess = @() } New-ModuleManifest @Manifest #Update-ModuleManifest @Manifest if ($Configuration.Information.Versioning.Prerelease -ne '') { #$FilePathPSD1 = Get-Item -Path $Configuration.Information.Manifest.Path $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.Information.Versioning.Prerelease $Data | Export-PSData -DataFile $Configuration.Information.Manifest.Path } Write-Verbose "Converting $($Configuration.Information.Manifest.Path)" (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) 2018. 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-PrepareModule { [CmdletBinding()] param ( [System.Collections.IDictionary] $Configuration, [string] $FullProjectPath # overwrites settings ) Begin { if (-not $Configuration) { return } [string] $FullModulePath = [IO.path]::Combine($Configuration.Information.DirectoryModules, $Configuration.Information.ModuleName) [string] $FullModulePathDelete = [IO.path]::Combine($Configuration.Information.DirectoryModules, $Configuration.Information.ModuleName) [string] $FullTemporaryPath = [IO.path]::GetTempPath() + '' + $Configuration.Information.ModuleName if ($FullProjectPath -eq '') { $FullProjectPath = [IO.Path]::Combine($Configuration.Information.DirectoryProjects, $Configuration.Information.ModuleName) } [string] $ProjectName = $Configuration.Information.ModuleName Write-Verbose '----------------------------------------------------' Write-Verbose "Project Name: $ProjectName" Write-Verbose "Full module path: $FullModulePath" Write-Verbose "Full project path: $FullProjectPath" Write-Verbose "Full module path to delete: $FullModulePathDelete" Write-Verbose "Full temporary path: $FullTemporaryPath" Write-Verbose "PSScriptRoot: $PSScriptRoot" Write-Verbose '----------------------------------------------------' $CurrentLocation = (Get-Location).Path Set-Location -Path $FullProjectPath Remove-Directory $FullModulePathDelete Remove-Directory $FullModulePath Remove-Directory $FullTemporaryPath Add-Directory $FullModulePath Add-Directory $FullTemporaryPath $DirectoryTypes = 'Public', 'Private', 'Lib', 'Bin', 'Enums', 'Images', 'Templates', 'Resources' $LinkDirectories = @() $LinkPrivatePublicFiles = @() } Process { if ($Configuration.Steps.BuildModule) { $Directories = Get-ChildItem -Path $FullProjectPath -Directory -Recurse foreach ($directory in $Directories) { $RelativeDirectoryPath = (Resolve-Path -LiteralPath $directory.FullName -Relative).Replace('.\', '') $RelativeDirectoryPath = "$RelativeDirectoryPath\" foreach ($LookupDir in $DirectoryTypes) { #Write-Verbose "New-PrepareModule - RelativeDirectoryPath: $RelativeDirectoryPath LookupDir: $LookupDir\" if ($RelativeDirectoryPath -like "$LookupDir\*" ) { $LinkDirectories += Add-ObjectTo -Object $RelativeDirectoryPath -Type 'Directory List' } } } $Files = Get-ChildItem -Path $FullProjectPath -File -Recurse $AllFiles = @() foreach ($File in $Files) { $RelativeFilePath = (Resolve-Path -LiteralPath $File.FullName -Relative).Replace('.\', '') $AllFiles += $RelativeFilePath } $RootFiles = @() $Files = Get-ChildItem -Path $FullProjectPath -File foreach ($File in $Files) { $RelativeFilePath = (Resolve-Path -LiteralPath $File.FullName -Relative).Replace('.\', '') $RootFiles += $RelativeFilePath } $LinkFilesRoot = @() # Link only files in Root Directory foreach ($File in $RootFiles) { switch -Wildcard ($file) { '*.psd1' { #Write-Color $File -Color Red $LinkFilesRoot += Add-ObjectTo -Object $File -Type 'Root Files List' } '*.psm1' { # Write-Color $File.FulllName -Color Red $LinkFilesRoot += Add-ObjectTo -Object $File -Type 'Root Files List' } 'License*' { $LinkFilesRoot += Add-ObjectTo -Object $File -Type 'Root Files List' } } } # Link only files from subfolers foreach ($file in $AllFiles) { switch -Wildcard ($file) { "*.dll" { $LinkPrivatePublicFiles += Add-FilesWithFolders -file $file -FullProjectPath $FullProjectPath -directory 'Lib' } "*.exe" { $LinkPrivatePublicFiles += Add-FilesWithFolders -file $file -FullProjectPath $FullProjectPath -directory 'Bin' } '*.ps1' { $LinkPrivatePublicFiles += Add-FilesWithFolders -file $file -FullProjectPath $FullProjectPath -directory 'Private', 'Public', 'Enums' } '*license*' { $LinkPrivatePublicFiles += Add-FilesWithFolders -file $file -FullProjectPath $FullProjectPath -directory 'Lib', 'Resources' } '*.jpg' { $LinkPrivatePublicFiles += Add-FilesWithFolders -file $file -FullProjectPath $FullProjectPath -directory 'Images', 'Resources' } '*.png' { $LinkPrivatePublicFiles += Add-FilesWithFolders -file $file -FullProjectPath $FullProjectPath -directory 'Images', 'Resources' } '*.xml' { $LinkPrivatePublicFiles += Add-FilesWithFolders -file $file -FullProjectPath $FullProjectPath -directory 'Templates' } '*.docx' { $LinkPrivatePublicFiles += Add-FilesWithFolders -file $file -FullProjectPath $FullProjectPath -directory 'Templates' } '*.js' { $LinkPrivatePublicFiles += Add-FilesWithFolders -file $file -FullProjectPath $FullProjectPath -directory 'Resources' } '*.css' { $LinkPrivatePublicFiles += Add-FilesWithFolders -file $file -FullProjectPath $FullProjectPath -directory 'Resources' } '*.rcs' { $LinkPrivatePublicFiles += Add-FilesWithFolders -file $file -FullProjectPath $FullProjectPath -directory 'Resources' } '*.gif' { $LinkPrivatePublicFiles += Add-FilesWithFolders -file $file -FullProjectPath $FullProjectPath -directory 'Resources' } '*.html' { $LinkPrivatePublicFiles += Add-FilesWithFolders -file $file -FullProjectPath $FullProjectPath -directory 'Resources' } } } if ($Configuration.Information.Manifest) { $Functions = Get-FunctionNamesFromFolder -FullProjectPath $FullProjectPath -Folder $Configuration.Information.FunctionsToExport if ($Functions) { Write-Verbose "Functions export: $Functions" $Configuration.Information.Manifest.FunctionsToExport = $Functions } $Aliases = Get-FunctionAliasesFromFolder -FullProjectPath $FullProjectPath -Folder $Configuration.Information.AliasesToExport if ($Aliases) { Write-Verbose "Aliases export: $Aliases" $Configuration.Information.Manifest.AliasesToExport = $Aliases } if (-not [string]::IsNullOrWhiteSpace($Configuration.Information.ScriptsToProcess)) { if (-not $Configuration.Options.Merge.Use) { $StartsWithEnums = "$($Configuration.Information.ScriptsToProcess)\" $FilesEnums = $LinkPrivatePublicFiles | Where-Object { ($_).StartsWith($StartsWithEnums) } if ($FilesEnums.Count -gt 0) { Write-Verbose "ScriptsToProcess export: $FilesEnums" $Configuration.Information.Manifest.ScriptsToProcess = $FilesEnums } } } New-PersonalManifest -Configuration $Configuration -ManifestPath $FullProjectPath\$ProjectName.psd1 -AddScriptsToProcess } if ($Configuration.Options.Merge.Use) { foreach ($Directory in $LinkDirectories) { $Dir = "$FullTemporaryPath\$Directory" Add-Directory $Dir } # Workaround to link files that are not ps1/psd1 $LinkDirectoriesWithSupportFiles = $LinkDirectories | Where-Object { $_ -ne 'Public\' -and $_ -ne 'Private\' } foreach ($Directory in $LinkDirectoriesWithSupportFiles) { $Dir = "$FullModulePath\$Directory" Add-Directory $Dir } Write-Verbose '[+] Linking files from Root Dir' Set-LinkedFiles -LinkFiles $LinkFilesRoot -FullModulePath $FullTemporaryPath -FullProjectPath $FullProjectPath Write-Verbose '[+] Linking files from Sub Dir' Set-LinkedFiles -LinkFiles $LinkPrivatePublicFiles -FullModulePath $FullTemporaryPath -FullProjectPath $FullProjectPath # Workaround to link files that are not ps1/psd1 $FilesToLink = $LinkPrivatePublicFiles | Where-Object { $_ -notlike '*.ps1' -and $_ -notlike '*.psd1' } Set-LinkedFiles -LinkFiles $FilesToLink -FullModulePath $FullModulePath -FullProjectPath $FullProjectPath if (-not [string]::IsNullOrWhiteSpace($Configuration.Information.LibrariesCore)) { $StartsWithCore = "$($Configuration.Information.LibrariesCore)\" $FilesLibrariesCore = $LinkPrivatePublicFiles | Where-Object { ($_).StartsWith($StartsWithCore) } #$FilesLibrariesCore } if (-not [string]::IsNullOrWhiteSpace($Configuration.Information.LibrariesDefault)) { $StartsWithDefault = "$($Configuration.Information.LibrariesDefault)\" $FilesLibrariesDefault = $LinkPrivatePublicFiles | Where-Object { ($_).StartsWith($StartsWithDefault) } #$FilesLibrariesDefault } 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 } else { foreach ($Directory in $LinkDirectories) { $Dir = "$FullModulePath\$Directory" Add-Directory $Dir } Write-Verbose '[+] Linking files from Root Dir' Set-LinkedFiles -LinkFiles $LinkFilesRoot -FullModulePath $FullModulePath -FullProjectPath $FullProjectPath Write-Verbose '[+] Linking files from Sub Dir' Set-LinkedFiles -LinkFiles $LinkPrivatePublicFiles -FullModulePath $FullModulePath -FullProjectPath $FullProjectPath #Set-LinkedFiles -LinkFiles $LinkFilesSpecial -FullModulePath $PrivateProjectPath -FullProjectPath $AddPrivate -Delete #Set-LinkedFiles -LinkFiles $LinkFiles -FullModulePath $FullModulePath -FullProjectPath $FullProjectPath } } if ($Configuration.Steps.PublishModule) { if ($Configuration.Options.PowerShellGallery.FromFile) { $ApiKey = Get-Content -Path $Configuration.Options.PowerShellGallery.ApiKey New-PublishModule -ProjectName $Configuration.Information.ModuleName -ApiKey $ApiKey } else { New-PublishModule -ProjectName $Configuration.Information.ModuleName -ApiKey $Configuration.Options.PowerShellGallery.ApiKey } } if ($Configuration.Publish.Use) { New-PublishModule -ProjectName $Configuration.Information.ModuleName -ApiKey $Configuration.Publish.ApiKey } } end { # Revers Path to current locatikon Set-Location -Path $CurrentLocation # Import Modules Section if ($Configuration) { if ($Configuration.Options.ImportModules.RequiredModules) { foreach ($Module in $Configuration.Information.Manifest.RequiredModules) { Import-Module -Name $Module -Force } } if ($Configuration.Options.ImportModules.Self) { Import-Module -Name $ProjectName -Force } if ($Configuration.Steps.BuildDocumentation) { $DocumentationPath = "$FullProjectPath\$($Configuration.Options.Documentation.Path)" $ReadMePath = "$FullProjectPath\$($Configuration.Options.Documentation.PathReadme)" Write-Verbose "Generating documentation to $DocumentationPath with $ReadMePath" if (-not (Test-Path -Path $DocumentationPath)) { 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 #-Verbose } else { $null = New-MarkdownHelp -Module $ProjectName -WithModulePage -OutputFolder $DocumentationPath #-ModuleName $ProjectName #-ModulePagePath $ReadMePath $null = Move-Item -Path "$DocumentationPath\$ProjectName.md" -Destination $ReadMePath #Start-Sleep -Seconds 1 # this is temporary workaround - due to diff output on update if ($Configuration.Options.Documentation.UpdateWhenNew) { $null = Update-MarkdownHelpModule $DocumentationPath -RefreshModulePage -ModulePagePath $ReadMePath #-Verbose } # } } } } } function New-PSMFile { param( [string] $Path, [string[]] $FunctionNames, [string[]] $FunctionAliaes, [Array] $LibrariesCore, [Array] $LibrariesDefault ) 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) { $Output = 'Add-Type -Path $PSScriptRoot\' + $File $Output | Add-Content -Path $Path } '} else {' | Add-Content -Path $Path foreach ($File in $LibrariesDefault) { $Output = 'Add-Type -Path $PSScriptRoot\' + $File $Output | Add-Content -Path $Path } '}' | Add-Content -Path $Path } elseif ($LibrariesCore.Count -gt 0) { foreach ($File in $LibrariesCore) { $Output = 'Add-Type -Path $PSScriptRoot\' + $File $Output | Add-Content -Path $Path } } elseif ($LibrariesDefault.Count -gt 0) { foreach ($File in $LibrariesDefault) { $Output = 'Add-Type -Path $PSScriptRoot\' + $File $Output | Add-Content -Path $Path } } @" Export-ModuleMember `` -Function @($Functions) `` -Alias @($Aliases) "@ | Add-Content -Path $Path } function New-PublishModule($projectName, $apikey) { Publish-Module -Name $projectName -Repository PSGallery -NuGetApiKey $apikey -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 ) #requires -version 3 # Make sure that the .NET framework sees the same working dir. as PS # and resolve the input path to a full path. [System.IO.Directory]::SetCurrentDirectory($PWD) # Caveat: .NET Core doesn't support [Environment]::CurrentDirectory $LiteralPath = [IO.Path]::GetFullPath($LiteralPath) # If -NoClobber was specified, throw an exception if the target file already # exists. if ($NoClobber -and (Test-Path $LiteralPath)) { Throw [IO.IOException] "The file '$LiteralPath' already exists." } # Create a StreamWriter object. # Note that we take advantage of the fact that the StreamWriter class by default: # - uses UTF-8 encoding # - without a BOM. $sw = New-Object IO.StreamWriter $LiteralPath, $Append $htOutStringArgs = @{} if ($Width) { $htOutStringArgs += @{ Width = $Width } } # Note: By not using begin / process / end blocks, we're effectively running # in the end block, which means that all pipeline input has already # been collected in automatic variable $Input. # We must use this approach, because using | Out-String individually # in each iteration of a process block would format each input object # with an indvidual header. try { $Input | Out-String -Stream @htOutStringArgs | % { $sw.WriteLine($_) } } finally { $sw.Dispose() } } function Remove-Directory { [CmdletBinding()] param ( [string] $Dir ) if (-not [string]::IsNullOrWhiteSpace($Dir)) { $exists = Test-Path -Path $Dir if ($exists) { #Write-Color 'Removing directory ', $dir -Color White, Yellow Remove-Item $dir -Confirm:$false -Recurse } else { #Write-Color 'Removing directory ', $dir, ' skipped.' -Color White, Yellow, Red } } } function Set-LinkedFiles { [CmdletBinding()] param( $LinkFiles, $FullModulePath, $FullProjectPath, [switch] $Delete ) foreach ($file in $LinkFiles) { $Path = "$FullModulePath\$file" $Path2 = "$FullProjectPath\$file" if ($Delete) { if (Test-ReparsePoint -path $Path) { # Write-Color 'Removing symlink first ', $path -Color White, Yellow Write-Verbose "Removing symlink first $path" Remove-Item $Path -Confirm:$false } } Write-Verbose "Creating symlink from $path2 (source) to $path (target)" #Write-Color 'Creating symlink from ', $path2, ' (source) to ', $path, ' (target)' -Color White, Yellow, White, Yellow, White $linkingFiles = cmd /c mklink $path $path2 } } 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 { <# .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, # Returns the content as a script block, rather than a string [Alias('ScriptBlock')] [switch]$AsScriptBlock, # If set, items in the hashtable will be sorted alphabetically [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]) { #region Indent $scriptString = "" $indent = $depth * 4 $scriptString+= "@{ " #endregion Indent #region Include $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 # Write-Verbose "$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 } #endregion Include } } } Export-ModuleMember ` -Function @('New-PrepareModule') ` -Alias @() |