pf-module.ps1
$ErrorActionPreference = 'stop' function Initialize-File_InNotPresent($path) { if (-not (Test-Path -Path $path)) { Set-Content -Path $path -Value "" } } function Remove-Alias { Param( [Parameter(ValueFromPipeline=$true)]$name, [Switch]$NoResolvedCommand ) process { If ($name -and ( Test-Path Alias:$name)) {Remove-Item Alias:$name} } end { if ($NoResolvedCommand) { $toRemove = Get-Alias | Where-Object { -not $_.ResolvedCommand } $toRemove.name | Where-Object {$_} | Where-Object { Test-Path Alias:$_ } | ForEach-Object { Remove-Item Alias:$_ -Verbose } } } } function Remove-Alias:::Example { Remove-Alias -NoResolvedCommand } function Get-Functions { param( [Parameter(ValueFromPipeline=$true)]$path ) process { $path = $path.FullName ?? $path . $path return Get-Command | Where-Object ScriptBlock | Where-Object { $_.ScriptBlock.File -eq $path } } } function Get-ContentToInclude($functionName) { if ($functionName) { $header = get-command DefineModuleHeader $body = $header.ScriptBlock.ToString() $result = "#Include-Start $functionName`n$body`n#Include-end $functionName`n" } return $result } function Install-Module_BlockScope { Param( [string[]]$Name, [string[]]$repository = $Global:PSRepositoryLocal, [ScriptBlock]$script ) if ($Name) { $missingModules = $Name | ForEach-Object { if (-not (Get-Module $_ -ListAvailable -SkipEditionCheck)) { $_ } } } try { if ($missingModules) { $moduleToRemove = $missingModules | ForEach-Object { $repositorySelected = if ($repository) { $repository } else { $candidateModules = find-module $_ | Sort-Object PublishedDate -Descending | Select-Object -First 1 $candidateModules.Repository } Install-Module -Name $_ -Repository $repositorySelected -PassThru -AllowClobber } } $script.InvokeReturnAsIs() } finally { if ($moduleToRemove) { $moduleToRemove | Uninstall-Module } } } function Install-Module_BlockScope:::Test { $moduleName = "pf-basic" Get-Module -Name $moduleName -ListAvailable | should -BeNullOrEmpty $result = Install-Module_BlockScope -Name $moduleName -script { $mm = Get-Module -Name $moduleName -ListAvailable $mm | should -not -BeNullOrEmpty Write-Host $mm Write-Output "Imported" }.GetNewClosure() write-host $result Get-Module -Name $moduleName -ListAvailable | should -BeNullOrEmpty } function Remove-PowershellComments($path) { $content = Get-Content -Path $path -Raw $result = Remove-PowershellCommentsFromString -content $content Set-Content -Path $path -Value $result -NoNewline } function Remove-PowershellCommentsFromString($content) { $result = $content -replace '\s*#(?!\brequires\b).*', '' $result = $result.Trim() return $result } function Remove-PowershellCommentsFromString:::Test($content) { Remove-PowershellCommentsFromString -content "#comment" | assert -eq "" Remove-PowershellCommentsFromString -content "#requires" | assert -eq "#requires" Remove-PowershellCommentsFromString -content " #comment abc" | assert -eq "" Remove-PowershellCommentsFromString -content "abc" | assert -eq " abc" } function Add-Module_PSModulePath ([string]$ModulesFolder = 'PSModules', [switch]$notRequired ) { $p = split-path -parent ($current = ( Get-PSCallStack )[0].ScriptName ) while ( $p -and -not ( Test-Path ( "$p\$ModulesFolder" ) ) ) { $p = Split-Path $p -Parent } if (-not $p -and -not $notRequired) { throw "Modules folder '$ModulesFolder' cannot be found starting from '$current'" } $p = Resolve-Path "$p\$ModulesFolder" if (-not $env:PSModulePath.Contains("$p;")) { $env:PSModulePath = "$p;$env:PSModulePath" } } function Get-Modulefolders ($path) { $folders = if ($path) { Get-ChildItem -Directory -path $path } else { Get-ChildItem -Directory } $folders | Where-Object name -NotLike '.*' } function Sync-ModuleManifests([switch]$all) { $commitCount = Get-GitCommitCount $folders = Get-Modules_Local | Get-Path if (-not $all) { $folders = $folders | Get-Git_Changed -folder } $folders | Initialize-Module_Manifest_EnsureFiles -Revision $commitCount } function Publish-ModuleEx { param( [Parameter(ValueFromPipeline=$true)]$modulePath, [string]$NuGetApiKey, [string[]]$repositoryList = @('LocalRepo') ) process { $moduleName = Split-Path $modulePath -Leaf $psd = Import-PowerShellDataFile -Path "$modulePath\$moduleName.psd1" foreach ($repository in $repositoryList) { $module = Find-Module -Name $moduleName -RequiredVersion $psd.ModuleVersion ` -ErrorAction SilentlyContinue -Repository $repository if (-not $module) { $additionalParams = @{} if ($NuGetApiKey) { $additionalParams.Add("NuGetApiKey",$NuGetApiKey) } Install-Module_BlockScope -script { Publish-Module @additionalParams -Path $modulePath -Repository $repository -Force }.GetNewClosure() } } $repository = $repositoryList[0] Uninstall-Module -Name $moduleName -ErrorAction SilentlyContinue # Install-Module -Name $moduleName -Scope CurrentUser -Repository $repository ` # -Force -AllowClobber -RequiredVersion $psd.ModuleVersion } } function Publish-ModuleEx:::Example { $folderModules = Get-Modulefolders -path C:\code\PowerFrame $folderModules.FullName | Initialize-Module_Manifest_EnsureFiles $folderModules.FullName | Publish-ModuleEx } function Get-ScriptCommandDependencies { # based on https://mikefrobbins.com/2019/02/21/powershell-tokenizer-more-accurate-than-ast-in-certain-scenarios/ # see also https://mikefrobbins.com/2019/05/17/using-the-ast-to-find-module-dependencies-in-powershell-functions-and-scripts/ param ( [Parameter(ValueFromPipeline=$true)] $file, [Switch]$FailOnMissingCommand, [Switch]$IgnoreSameModule ) begin { $getCommandErrorAction = ($FailOnMissingCommand) ? 'Stop' : 'SilentlyContinue' } process { $file = $file | Get-Path $Token = $null $ignoredOutput = [System.Management.Automation.Language.Parser]::ParseFile($File, [ref]$Token, [ref]$null) Write-Verbose $ignoredOutput $commandTokenList = $Token | Where-Object {$_.TokenFlags -eq 'CommandName'} $commandNameList = $commandTokenList.Value.ToLowerInvariant() | Select-Object -Unique | Sort-Object $commandList = $commandNameList | ForEach-Object { Get-Command -name $_ -ErrorAction $getCommandErrorAction } $commandList = $commandList | Where-Object { $_.Module.Path } if ($IgnoreSameModule) { $commandList = $commandList | Where-Object { $cmdPath = $_.Module.Path $moduleFolder = split-path -Path $cmdPath -Parent -not (Test-Path_Parent -path $file -parent $moduleFolder) } } $commandList } } function Get-ScriptCommandDependencies:::Examples{ $result = Get-ChildItem -Path .\pf-AzPipelines -filter *.ps1 -Recurse | Get-ScriptCommandDependencies -IgnoreSameModule $result } function Get-ModuleDependencies { param ( [Parameter(ValueFromPipeline=$true)] $file, [Switch]$FailOnMissingCommand ) process { $commandList = $file | Get-ScriptCommandDependencies -IgnoreSameModule ` -FailOnMissingCommand:($FailOnMissingCommand.IsPresent) $commandList.Module } } function Get-ModuleDependencies:::Example { $result = Get-ChildItem -Path .\pf-AzPipelines -filter *.ps1 -Recurse | Get-ModuleDependencies $result } function Get-ScriptFunctions { param ( [Parameter(ValueFromPipeline=$true)] $file ) process { $file = $file.fullname ?? $file $Token = $null $ignoredOutput = [System.Management.Automation.Language.Parser]::ParseFile($File, [ref]$Token, [ref]$null) Write-Verbose $ignoredOutput $funcBegin = $false $Token | ForEach-Object { if ($funcBegin) { $_.Text } $funcBegin = ($_.Kind -like "function") } } } function Get-ScriptFunctions:::Example { Get-ChildItem pf-* -Recurse -Filter *.ps1 | Select-Object -First 1 | Get-ScriptFunctions } |