Public/New-FunctionTemplate.ps1

function New-FunctionTemplate {
    <#
    .SYNOPSIS
    Creates new function script files from the module's function template.

    .DESCRIPTION
    New-FunctionTemplate creates one <Name>.ps1 file per function name from the templates shipped in the
    module's Templates folder. The generated function has comment-based help, CmdletBinding, a
    begin/process/end structure and, unless -ExcludeTelemetryCollection is used, the tcs.core 0.4.0 telemetry
    pattern (Start-TcsTelemetry in begin; Complete-TcsTelemetry in end, and in the process block's finally
    when an item fails or a downstream command stops the pipeline). Functions with a state-changing verb
    (New, Set, Remove, Start, Stop, Restart, Reset, Update) get SupportsShouldProcess and a ShouldProcess check.

    .PARAMETER Name
    The name of each function to create, for example Get-Widget. Accepts pipeline input.

    .PARAMETER Path
    Directory where the new function file(s) will be created. Must be an existing directory. Defaults to
    the current location.

    .PARAMETER ExcludeTelemetryCollection
    Do not include the tcs.core telemetry calls in the generated function.

    .PARAMETER Force
    Overwrite an existing function file of the same name without asking.

    .PARAMETER PassThru
    Return the created file (System.IO.FileInfo) for each function generated.

    .EXAMPLE
    New-FunctionTemplate -Name 'Get-MyFunction' -Path './MyModule/Public'

    Creates Get-MyFunction.ps1 in ./MyModule/Public with the telemetry pattern.

    .EXAMPLE
    'Get-Widget', 'Set-Widget' | New-FunctionTemplate -Path './MyModule/Public' -ExcludeTelemetryCollection -Force -PassThru

    Creates or overwrites two function files without telemetry and returns the created files.

    .INPUTS
    System.String. Function names (Name) can be passed by pipeline.

    .OUTPUTS
    None by default. System.IO.FileInfo for each created file when -PassThru is used.

    .NOTES
    Author: TheCodeSaiyan
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    [OutputType([System.IO.FileInfo])]
    param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [ValidateNotNullOrEmpty()]
        [string[]]
        $Name,

        [ValidateScript({ Test-Path -Path $_ -PathType Container })]
        [string]
        $Path = $PWD.Path,

        [switch]
        $ExcludeTelemetryCollection,

        [switch]
        $Force,

        [switch]
        $PassThru
    )

    begin {
        $Telemetry = Start-TcsTelemetry

        $TemplateName = if ($ExcludeTelemetryCollection) { 'Function_Template.template' } else { 'Function_Template_Telemetry.template' }
        $TemplatePath = Join-Path -Path (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -ChildPath 'Templates') -ChildPath $TemplateName
        try {
            $Template = Get-Content -Path $TemplatePath -Raw -ErrorAction Stop
        }
        catch {
            Complete-TcsTelemetry -Token $Telemetry -ErrorRecord $_
            throw
        }
        $StateChangingVerbs = @('New', 'Set', 'Remove', 'Start', 'Stop', 'Restart', 'Reset', 'Update')
        $Requested = 0
        $Created = 0
        $Failures = 0
        $LastError = $null
    }
    process {
        $Completed = $false
        try {
            foreach ($FunctionName in $Name) {
                $Requested++
                $FunctionPath = Join-Path -Path $Path -ChildPath "$FunctionName.ps1"
                $Content = $Template.Replace('###TEMPLATE_FUNCTION_NAME', $FunctionName)
                $Verb = ($FunctionName -split '-', 2)[0]
                if ($Verb -in $StateChangingVerbs) {
                    # Functions that change state must support -WhatIf and -Confirm
                    $Content = $Content.Replace('[CmdletBinding()]', '[CmdletBinding(SupportsShouldProcess = $true)]')
                    $Content = $Content -replace '(?m)^([ \t]*)(Write-Verbose "Processing ''\$Name''\.")', ('$1if ($PSCmdlet.ShouldProcess($Name, ''' + $Verb + ''')) {' + "`n" + '$1 $2' + "`n" + '$1}')
                }

                if ((Test-Path -Path $FunctionPath) -and -not $Force) {
                    if (-not $PSCmdlet.ShouldContinue("The file '$FunctionPath' already exists. Do you want to overwrite it?", 'Confirm overwrite')) {
                        Write-Warning "The function '$FunctionPath' was skipped."
                        continue
                    }
                }
                if (-not $PSCmdlet.ShouldProcess($FunctionPath, 'Create function file')) {
                    continue
                }
                try {
                    Set-Content -Path $FunctionPath -Value $Content -NoNewline -Force -ErrorAction Stop
                    $Created++
                    if ($PassThru) {
                        Get-Item -Path $FunctionPath
                    }
                }
                catch {
                    $Failures++
                    $LastError = $_
                    Write-Error "The function '$FunctionPath' was not created. Error: $($_.Exception.Message)"
                }
            }
            $Completed = $true
        }
        catch {
            $LastError = $_
            throw
        }
        finally {
            # Also runs when a downstream command stops the pipeline, which skips the end block
            if (-not $Completed) {
                Complete-TcsTelemetry -Token $Telemetry -ErrorRecord $LastError
            }
        }
    }
    end {
        Write-Verbose "Created $Created out of $Requested function(s)."
        Complete-TcsTelemetry -Token $Telemetry -Failed:($Failures -gt 0) -ErrorRecord $LastError
    }
}