functions/public/Convert-ScriptToFunction.ps1
Function Convert-ScriptToFunction { [cmdletbinding()] [Outputtype("System.String")] [alias('csf')] Param( [Parameter( Position = 0, Mandatory, ValueFromPipelineByPropertyName, HelpMessage = "Enter the path to your PowerShell script file." )] [ValidateScript({Test-Path $_ })] [ValidatePattern("\.ps1$")] [string]$Path, [Parameter( Position = 1, Mandatory, ValueFromPipelineByPropertyName, HelpMessage = "What is the name of your new function?")] [ValidateScript({ if ($_ -match "^\w+-\w+$") { $true } else { Throw "Your function name should have a Verb-Noun naming convention" $False } })] [string]$Name, [Parameter(ValueFromPipelineByPropertyName,HelpMessage = "Specify an optional alias for your new function. You can define multiple aliases separated by commas.")] [ValidateNotNullOrEmpty()] [string[]]$Alias ) DynamicParam { <# If running this function in the PowerShell ISE or VS Code, define a ToEditor switch parameter #> If ($host.name -match "ISE|Code") { $paramDictionary = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary # Defining parameter attributes $attributeCollection = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute] $attributes = New-Object System.Management.Automation.ParameterAttribute $attributes.ParameterSetName = '__AllParameterSets' $attributeCollection.Add($attributes) # Defining the runtime parameter $dynParam1 = New-Object -Type System.Management.Automation.RuntimeDefinedParameter('ToEditor', [Switch], $attributeCollection) $paramDictionary.Add('ToEditor', $dynParam1) return $paramDictionary } # end if } #end DynamicParam Begin { Write-Verbose "Starting $($MyInvocation.MyCommand)" Write-Verbose "Initializing" $new = [System.Collections.Generic.list[string]]::new() } #begin Process { #normalize $Path = Convert-Path $path $Name = Format-FunctionName $Name Write-Verbose "Processing $path" $AST = _getAST $path if ($ast.extent) { Write-Verbose "Getting any comment based help" $ch = $astTokens | Where-Object { $_.kind -eq 'comment' -AND $_.text -match '\.synopsis' } if ($ast.ScriptRequirements) { Write-Verbose "Adding script requirements" if($ast.ScriptRequirements.RequiredPSVersion) { $new.Add("#requires -version $($ast.ScriptRequirements.RequiredPSVersion.ToString())") } if ($ast.ScriptRequirements.RequiredModules) { Foreach ($m in $ast.ScriptRequirements.RequiredModules) { #test for version requirements $ver = $m.psobject.properties.where({$_.name -match 'version' -AND $_.value}) if ($ver) { $new.Add("#requires -module @{ModuleName = '$($m.name)';$($ver.Name) = '$($ver.value)'}") } else { $new.add("#requires -module $($m.Name)") } } } if ($ast.ScriptRequirements.IsElevationRequired) { $new.Add("#requires -RunAsAdministrator") } If ($ast.ScriptRequirements.requiredPSEditions) { $new.add("#requires -PSEdition $($ast.ScriptRequirements.requiredPSEditions)") } $new.Add("`n") } else { Write-Verbose "No script requirements found" } $head = @" # Function exported from $Path Function $Name { "@ $new.add($head) if ($ch) { $new.Add($ch.text) $new.Add("`n") } else { Write-Verbose "Generating new comment based help from parameters" if ($ast.ParamBlock) { New-CommentHelp -ParamBlock $ast.ParamBlock | Foreach-Object { $new.Add("$_")} } else { New-CommentHelp -templateonly |Foreach-Object { $new.Add("$_")} } $new.Add("`n") } [regex]$rx = "\[cmdletbinding\(.*\)\]" if ($rx.Ismatch($ast.Extent.text)) { Write-Verbose "Using existing cmdletbinding" #use the first match $cb = $rx.match($ast.extent.text).Value $new.Add("`t$cb") } else { Write-Verbose "Adding [cmdletbinding()]" $new.Add("`t[cmdletbinding()]") } if ($alias) { Write-Verbose "Adding function alias definition $($alias -join ',')" $new.Add("`t[Alias('$($alias -join "','")')]") } if ($ast.ParamBlock) { Write-Verbose "Adding defined Param() block" [void]($ast.ParamBlock.tostring().split("`n").Foreach({$new.add("`t$_")}) -join "`n") $new.Add("`n") } else { Write-Verbose "Adding Param() block" $new.add("`tParam()") } if ($ast.DynamicParamBlock) { #assumes no more than 1 dynamic parameter Write-Verbose "Adding dynamic parameters" [void]($ast.DynamicParamBlock.tostring().split("`n").Foreach({$new.Add($_)}) -join "`n") } if ($ast.BeginBlock.Extent.text) { Write-Verbose "Adding defined Begin block" [void]($ast.BeginBlock.Extent.toString().split("`n").Foreach({$new.Add($_)}) -join "`n") $UseBPE = $True } if ($ast.ProcessBlock.Extent.text) { Write-Verbose "Adding defined Process block" [void]($ast.ProcessBlock.Extent.ToString().split("`n").Foreach({$new.add($_) }) -join "`n") } if ($ast.EndBlock.Extent.text) { if ($UseBPE) { Write-Verbose "Adding opening End{} block" $new.Add("`tEnd {") } Write-Verbose "Adding the remaining code or defined endblock" [void]($ast.Endblock.Statements.foreach({ $_.tostring() }).Foreach({ $new.Add($_)})) if ($UseBPE) { Write-Verbose "Adding closing End {} block" $new.Add("`t}") } } else { $new.Add("End { }") } Write-Verbose "Closing the function" $new.Add( "`n} #close $name") if ($PSBoundParameters.ContainsKey("ToEditor")) { Write-Verbose "Opening result in editor" if ($host.name -match "ISE") { $newfile = $psise.CurrentPowerShellTab.Files.add() $newfile.Editor.InsertText(($new -join "`n")) $newfile.editor.select(1,1,1,1) } elseif ($host.name -match "Code") { $pseditor.Workspace.NewFile() $ctx = $pseditor.GetEditorContext() $ctx.CurrentFile.InsertText($new -join "`n") } else { $new -join "`n" | Set-Clipboard Write-Warning "Can't detect the PowerShell ISE or VS Code. Output has been copied to the clipboard." } } else { Write-Verbose "Writing output [$($new.count) lines] to the pipeline" $new -join "`n" } } #if ast found else { Write-Warning "Failed to find a script body to convert to a function." } } #process End { Write-Verbose "Ending $($MyInvocation.mycommand)" } } |