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 telemetry
    pattern (Start in begin, End on success and End -Failed on error). 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 {
        $TelemetryArgs = @{
            ModuleName    = $MyInvocation.MyCommand.Module.Name
            ModuleVersion = [string]$MyInvocation.MyCommand.Module.Version
            CommandName   = $MyInvocation.MyCommand.Name
            ExecutionID   = [guid]::NewGuid().ToString()
        }
        Invoke-TelemetryCollection @TelemetryArgs -Stage Start -ClearTimer

        $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 {
            Invoke-TelemetryCollection @TelemetryArgs -Stage End -Failed $true -Exception $_
            throw
        }
        $StateChangingVerbs = @('New', 'Set', 'Remove', 'Start', 'Stop', 'Restart', 'Reset', 'Update')
        $Requested = 0
        $Created = 0
        $Failures = 0
    }
    process {
        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++
                Write-Error "The function '$FunctionPath' was not created. Error: $($_.Exception.Message)"
            }
        }
    }
    end {
        Write-Verbose "Created $Created out of $Requested function(s)."
        Invoke-TelemetryCollection @TelemetryArgs -Stage End -Failed ($Failures -gt 0)
    }
}