stitch.psm1

#===============================================================================
#region prefix

# Generated by stitch build system 2023-06-12 18:54:08
#endregion prefix
#===============================================================================

using namespace Markdig
using namespace Markdig.Syntax
using namespace System.Management.Automation.Language
using namespace System.Diagnostics.CodeAnalysis
using namespace System.Collections.Specialized

.Syntax

#===============================================================================
#region enum
#region source\stitch\enum\ModuleFlag.ps1 1
[Flags()]
enum ModuleFlag {
    None        = 0x00
    HasManifest = 0x01
    HasModule   = 0x02
}
#endregion source\stitch\enum\ModuleFlag.ps1 6
#endregion enum
#===============================================================================

#===============================================================================
#region Private functions
#region source\stitch\private\Changelog\Format-ChangelogEntry.ps1 2
function Format-ChangelogEntry {
    <#
    .SYNOPSIS
        Format the entry text by replacing tokens from the config file with their values
    .DESCRIPTION
        Format-ChangelogEntry uses the Format.Entry line from the config file to format the Entry line in the
        Changelog.
        The following fields are available in an Entry:
        | Field | Pattern |
        |-------------|---------------|
        | Description | `{desc}` |
        | Type | `{type}` |
        | Scope | `{scope}` |
        | Title | `{title}` |
        | Sha | `{sha}` |
        | Author | `{author}` |
        | Email | `{email}` |
        | Footer | `{ft.<name>}` |
    .EXAMPLE
        $Entry | Format-ChangelogEntry
    #>

    [CmdletBinding()]
    param(
        # Information about the Entry (commit) object
        [Parameter(
            ValueFromPipeline
        )]
        [object]$EntryInfo
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"

        $DEFAULT_FORMAT = '- {sha} {desc} ({author})'
        $DEFAULT_BREAKING_FORMAT = '- {sha} **breaking change** {desc} ({author})'

        $config = Get-ChangelogConfig

        $descriptionPattern = '\{desc\}'
        $typePattern = '\{type\}'
        $scopePattern = '\{scope\}'
        $titlePattern = '\{title\}'
        $shaPattern = '\{sha\}'
        $authorPattern = '\{author\}'
        $emailPattern = '\{email\}'
        $footerPattern = '\{ft\.(\w+)\}'

    } process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"

        if ($EntryInfo.IsBreakingChange) {
            $formatOptions = $config.Format.BreakingChange ?? $DEFAULT_BREAKING_FORMAT
        } else {
            $formatOptions = $config.Format.Entry ?? $DEFAULT_FORMAT
        }

        $format = $formatOptions -replace $descriptionPattern , $EntryInfo.Description
        $format = $format -replace $typePattern , $EntryInfo.Type
        $format = $format -replace $scopePattern , $EntryInfo.Scope
        $format = $format -replace $titlePattern , $EntryInfo.Title
        $format = $format -replace $shaPattern , $EntryInfo.ShortSha
        $format = $format -replace $authorPattern , $EntryInfo.Author.Name
        $format = $format -replace $emailPattern , $EntryInfo.Author.Email


        if ($format -match $footerPattern) {
            if ($matches.Count -gt 0) {
                if ($EntryInfo.Footers[$Matches.1]) {
                    $format = $format -replace "\{ft\.$($Matches.1)\}", ($EntryInfo.Footers[$Matches.1] -join ', ')
                }
            }
        }

        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    } end {
        $format
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\private\Changelog\Format-ChangelogEntry.ps1 79
#region source\stitch\private\Changelog\Format-ChangelogFooter.ps1 3
function Format-ChangelogFooter {
    <#
    .SYNOPSIS
        Format the footer in the Changelog
    .EXAMPLE
        Format-ChangelogFooter
    #>

    [OutputType([string])]
    [CmdletBinding()]
    param(
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $DEFAULT_FORMAT = ''
        $config = Get-ChangelogConfig

        if (-not([string]::IsNullorEmpty($config.Footer))) {
            $formatOptions = $config.Footer
        } else {
            $formatOptions = $DEFAULT_FORMAT
        }
    } process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        #! There are no replacements in the footer yet
        $format = $formatOptions
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    } end {
        $format
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\private\Changelog\Format-ChangelogFooter.ps1 33
#region source\stitch\private\Changelog\Format-ChangelogGroup.ps1 2
function Format-ChangelogGroup {
    <#
    .SYNOPSIS
        Format the heading of a group of changelog entries
    .EXAMPLE
        $group | Format-ChangelogGroup
    #>

    [OutputType([string])]
    [CmdletBinding()]
    param(
        # A table of information about a changelog group
        [Parameter(
            ValueFromPipeline
        )]
        [object]$GroupInfo
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"

        $DEFAULT_FORMAT = '### {name}'

        $config = Get-ChangelogConfig

        $formatOptions = ($config.Format.Group ?? $DEFAULT_FORMAT)
        $namePattern = '\{name\}'
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        Write-Debug "Format was '$formatOptions'"
        Write-Debug "GroupInfo is $($GroupInfo | ConvertTo-Psd)"
        if (-not ([string]::IsNullorEmpty($GroupInfo.DisplayName))) {
            Write-Debug " - DisplayName is $($GroupInfo.DisplayName)"
            $format = $formatOptions -replace $namePattern, $GroupInfo.DisplayName
        } elseif (-not ([string]::IsNullorEmpty($GroupInfo.Name))) {
            $format = $formatOptions -replace $namePattern, $GroupInfo.Name
            Write-Debug " - Name is $($GroupInfo.Name)"
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "Format is '$format'"
        $format
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\private\Changelog\Format-ChangelogGroup.ps1 46
#region source\stitch\private\Changelog\Format-ChangelogHeader.ps1 3
function Format-ChangelogHeader {
    <#
    .SYNOPSIS
        Format the header in the Changelog
    .EXAMPLE
        Format-ChangelogHeader
    #>

    [OutputType([string])]
    [CmdletBinding()]
    param(
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $DEFAULT_FORMAT = ''
        $config = Get-ChangelogConfig

        if (-not([string]::IsNullorEmpty($config.Header))) {
            $formatOptions  = $config.Header
        } else {
            $formatOptions = $DEFAULT_FORMAT
        }
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        #! There are no replacements in the header yet
        $format = $formatOptions
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        $format
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\private\Changelog\Format-ChangelogHeader.ps1 35
#region source\stitch\private\Changelog\Format-ChangelogRelease.ps1 2
function Format-ChangelogRelease {
    <#
    .SYNOPSIS
        Format the heading for a release in the changelog by replacing tokens form the config file with thier values
    .DESCRIPTION
        Format-ChangelogRelease uses the Format.Release line from the config file to format the Release heading in
        the Changelog.
        The following fields are available in a Release:
 
        | Field | Pattern |
        |-------------------|--------------------------|
        | Name | `{name}` |
        | Date | `{date}` |
        | Date with Format | `{date yyyy-MM-dd}` |
 
    >EXAMPLE
        $release | Format-ChangelogRelease
 
    #>

    [CmdletBinding()]
    param(
        # A table of information about a release
        [Parameter(
            ValueFromPipeline
        )]
        [object]$ReleaseInfo
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"

        $DEFAULT_FORMAT = '## [{name}] - {date yyyy-MM-dd}'

        $config = Get-ChangelogConfig

        $formatOptions = $config.Format.Release ?? $DEFAULT_FORMAT
        $namePattern = '\{name\}'
        $datePattern = '\{date\}'
        $dateFormatPattern = '\{date (?<df>.*?)\}'

    } process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"

        Write-Debug " Items: $($ReleaseInfo.Keys)"
        $format = $formatOptions -replace $namePattern, $ReleaseInfo.Name

        # date
        if (-not([string]::IsNullorEmpty($ReleaseInfo.Timestamp))) {
            if ($format -match $dateFormatPattern) {
                if ($ReleaseInfo.Timestamp -is [System.DateTimeOffset]) {
                    $dateText = (Get-Date $ReleaseInfo.Timestamp.UtcDateTime -Format $dateFormat)
                } else {
                    $dateText = (Get-Date $ReleaseInfo.Timestamp -Format $dateFormat)
                }

                $dateField = $Matches.0 # we want to replace the whole field so store that
                $dateFormat = $Matches.df # the format of the datetime object

                $format = $format -replace $dateField , $dateText
            } else {
                $format = $format -replace $datePattern, $ReleaseInfo.Timestamp
            }
        }

        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    } end {
        $format
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\private\Changelog\Format-ChangelogRelease.ps1 70
#region source\stitch\private\Changelog\Format-HeadingText.ps1 2
function Format-HeadingText {
    <#
    .SYNOPSIS
        If the given Heading Block is a LinkInline, recreate the markdown link text, if not return the headings
        content
    .EXAMPLE
        $heading | Format-HeadingText
    .EXAMPLE
        $heading | Format-HeadingText -NoLink
    #>

    [CmdletBinding()]
    param(
        [Parameter(
            ValueFromPipeline
        )]
        [Markdig.Syntax.HeadingBlock]$Heading,

        # Return the text only without link markup
        [Parameter(
        )]
        [switch]$NoLink
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $headingText = ''
    }
    process {
        $child = $Heading.Inline.FirstChild
        while ($null -ne $child) {
            if ($child -is [Markdig.Syntax.Inlines.LinkInline]) {
                if ($NoLink) {
                    $headingText = $child.FirstChild.Content.ToString()
                }else {
                    Write-Debug ' - creating link text'
                    $headingText += ( -join ('[', $child.FirstChild.Content.ToString(), ']'))
                    Write-Debug " - $headingText"
                    $headingText += ( -join ('(', $child.Url, ')' ))
                    Write-Debug " - $headingText"
                }
            } else {
                $headingText += $child.Content.ToString()
            }
            $child = $child.NextSibling
        }
    }
    end {
        $headingText
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }

}
#endregion source\stitch\private\Changelog\Format-HeadingText.ps1 52
#region source\stitch\private\Changelog\Resolve-ChangelogGroup.ps1 2
function Resolve-ChangelogGroup {
    <#
    .SYNOPSIS
        Given a git commit and a configuration identify what group the commit should be in
    .EXAMPLE
        Get-GitCommit | ConvertFrom-ConventionalCommit | Resolve-ChangelogGroup
    #>

    [CmdletBinding()]
    param(
        # A conventional commit object
        [Parameter(
            ValueFromPipeline
        )]
        [PSTypeName('Git.ConventionalCommitInfo')][Object[]]$Commit
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $config = Get-ChangelogConfig
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        Write-Debug "Processing Commit : $($Commit.Title)"
        foreach ($key in $config.Groups.Keys) {
            $group = $config.Groups[$key]
            $display = $group.DisplayName ?? $key
            $group['Name'] = $key
            Write-Debug "Checking group $key"
            switch ($group.Keys) {
                'Type' {
                    if (($null -ne $Commit.Type) -and
                        ($group.Type.Count -gt 0)) {
                        Write-Debug " - Has Type entries"
                        foreach ($type in $group.Type) {
                            Write-Debug " - Checking for a match with $type"
                            if ($Commit.Type -match $type) {
                                return $group
                            }
                        }
                    }
                    continue
                }
                'Title' {
                    if (($null -ne $Commit.Title) -and
                        ($group.Title.Count -gt 0)) {
                        Write-Debug " - Has Title entries"
                        foreach ($title in $group.Title) {
                            Write-Debug " - Checking for a match with $title"
                            if ($Commit.Title -match $title) {
                                return $group
                            }
                        }
                    }
                    continue
                }
                'Scope' {
                    if (($null -ne $Commit.Scope) -and
                        ($group.Scope.Count -gt 0)) {
                        Write-Debug " - Has Scope entries"
                        foreach ($scope in $group.Scope) {
                            Write-Debug " - Checking for a match with $scope"
                            if ($Commit.Scope -match $scope) {
                                return $group
                            }
                        }
                    }
                    continue
                }
            }
        }

        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\private\Changelog\Resolve-ChangelogGroup.ps1 77
#region source\stitch\private\FeatureFlags\Disable-FeatureFlag.ps1 1
function Disable-FeatureFlag {
    [CmdletBinding()]
    param(
        # The name of the feature flag to disable
        [Parameter(
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [ValidateNotNullOrEmpty()]
        [string[]]$Name,

        # The description of the feature flag
        [Parameter(
        )]
        [string]$Description
    )
    begin {
        #TODO: I'm relying on BuildInfo, because I don't see a scenario right now where we would use this without it
    }
    process {
        if ($null -ne $BuildInfo) {
            if ($BuildInfo.Keys -contains 'Flags') {
                if ($BuildInfo.Flags.ContainsKey($Name)) {
                    $BuildInfo.Flags[$Name].Enabled = $true
                } else {
                    $BuildInfo.Flags[$Name] = @{
                        Enabled = $true
                        Description = $Description ?? "Missing description"
                    }
                }
            }
        }
    }
    end {
    }
}
#endregion source\stitch\private\FeatureFlags\Disable-FeatureFlag.ps1 37
#region source\stitch\private\FeatureFlags\Enable-FeatureFlag.ps1 1
function Enable-FeatureFlag {
    [CmdletBinding()]
    param(
        # The name of the feature flag to enable
        [Parameter(
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [ValidateNotNullOrEmpty()]
        [string[]]$Name,

        # The description of the feature flag
        [Parameter(
        )]
        [string]$Description
    )
    begin {
        #TODO: I'm relying on BuildInfo, because I don't see a scenario right now where we would use this without it
    }
    process {
        if ($null -ne $BuildInfo) {
            if ($BuildInfo.Keys -contains 'Flags') {
                if ($BuildInfo.Flags.ContainsKey($Name)) {
                    $BuildInfo.Flags[$Name].Enabled = $true
                } else {
                    $BuildInfo.Flags[$Name] = @{
                        Enabled = $true
                        Description = $Description ?? "Missing description"
                    }
                }
            }
        }
    }
    end {
    }
}
#endregion source\stitch\private\FeatureFlags\Enable-FeatureFlag.ps1 37
#region source\stitch\private\InvokeBuild\Invoke-TaskNameCompletion.ps1 1
function Invoke-TaskNameCompletion {
    <#
    .SYNOPSIS
        A tab completion provider for task names
    #>

    param(
        [Parameter(
            Mandatory
        )]
        [ArgumentCompleter(
            {
                param(
                    $commandName,
                    $parameterName,
                    $wordToComplete,
                    $commandAst,
                    $fakeBoundParameters
                )
                $possibleValues = Invoke-Build ? | Select-Object -ExpandProperty Name

                if ($fakeBoundParameters.ContainsKey('Name')) {
                    $possibleValues | Where-Object {
                        $_ -like "$wordToComplete*"
                    }
                } else {
                    $possibleValues | ForEach-Object { $_ }
                }
            })]$Name
    )
}
#endregion source\stitch\private\InvokeBuild\Invoke-TaskNameCompletion.ps1 30
#region source\stitch\private\Markdown\Add-MarkdownElement.ps1 1
function Add-MarkdownElement {
    [CmdletBinding()]
    param(
        # Markdown element(s) to add to the document
        [Parameter(
            Position = 0
        )]
        [Object]$Element,

        # The document to add the element to
        [Parameter(
            Position = 1,
            ValueFromPipeline
        )]
        [ref]$Document,

        # Index to insert the Elements to, append to end if not specified
        [Parameter(
            Position = 2
        )]
        [int]$Index,

        # Return the updated document to the pipeline
        [Parameter(
        )]
        [switch]$PassThru
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
    } end {
        if ($PSBoundParameters.ContainsKey('Index')) {
            $Document.Value.Insert($Index, $Element)
        } else {
            $Document.Value.Add($Element)
        }

# if ($PassThru) { $Document.Value | Write-Output -NoEnumerate }
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\private\Markdown\Add-MarkdownElement.ps1 42
#region source\stitch\private\Markdown\Get-MarkdownDescendant.ps1 4
function Get-MarkdownDescendant {
    [CmdletBinding()]
    param(
        [Parameter(
            ValueFromPipeline
        )]
        [MarkdownObject]$InputObject,

        # The type of element to return
        [Parameter(
            Position = 0
        )]
        [string]$TypeName
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        if ($PSBoundParameters.ContainsKey('TypeName')) {

            #Check Type
            $type = $TypeName -as [Type]
            if (-not $type) {
                throw "Type: '$TypeName' not found"
            }
            $methodDescendants = [MarkdownObjectExtensions].GetMethod('Descendants', 1, [MarkdownObject])
            $mdExtensionsType = [MarkdownObjectExtensions]
            $method = $methodDescendants.MakeGenericMethod($Type)
            $method.Invoke($mdExtensionsType, @(, $InputObject)) | ForEach-Object { $PSCmdlet.WriteObject($_, $false) }
        } else {
            [MarkdownObjectExtensions]::Descendants($InputObject)
        }

    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\private\Markdown\Get-MarkdownDescendant.ps1 41
#region source\stitch\private\Markdown\Get-MarkdownElement.ps1 1
function Get-MarkdownElement {
    [CmdletBinding()]
    param(
        [Parameter(
            ValueFromPipeline
        )]
        [Markdig.Syntax.MarkdownObject]$InputObject,

        # The type of element to return
        [Parameter(
            Position = 0
        )]
        [string]$TypeName
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {

    } end {
        #Check Type
        if ($TypeName -notmatch '^Markdig\.Syntax') {
            $TypeName = 'Markdig.Syntax.' + $TypeName
        }

        $type = $TypeName -as [Type]
        if (-not $type) {
            throw "Type: '$TypeName' not found"
        }
        Write-Verbose "Looking for a $type"
        foreach ($token in $InputObject) {
            if ($token -is $type) {
                $token | Write-Output
            }
        }
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\private\Markdown\Get-MarkdownElement.ps1 38
#region source\stitch\private\Markdown\Get-MarkdownFrontMatter.ps1 1
function Get-MarkdownFrontMatter {
    [CmdletBinding()]
    param(
        [Parameter(
            ValueFromPipeline
        )]
        [Markdig.Syntax.MarkdownDocument]$InputObject
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Get-MarkdownElement -InputObject $InputObject -TypeName 'Markdig.Extensions.Yaml.YamlFrontMatterBlock'
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }

}
#endregion source\stitch\private\Markdown\Get-MarkdownFrontMatter.ps1 19
#region source\stitch\private\Markdown\Get-MarkdownHeading.ps1 1
function Get-MarkdownHeading {
    [CmdletBinding()]
    param(
        [Parameter(
            ValueFromPipeline
        )]
        [Markdig.Syntax.MarkdownObject[]]$InputObject
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        if ($PSItem -is [Markdig.Syntax.HeadingBlock]) {
            $PSItem | Write-Output
        }
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }

}
#endregion source\stitch\private\Markdown\Get-MarkdownHeading.ps1 21
#region source\stitch\private\Markdown\Import-Markdown.ps1 2
function Import-Markdown {
    [CmdletBinding()]
    [OutputType([Markdig.Syntax.MarkdownDocument])]
    param(
        # A markdown file to be converted
        [Parameter(
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string]$Path,

        # Additional extensions to add
        # Note advanced and yaml already added
        [Parameter(
        )]
        [string[]]$Extension,

        # Enable track trivia
        [Parameter(
        )]
        [switch]$TrackTrivia
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        try {
            $content = Get-Content $Path -Raw
            $builder = New-Object Markdig.MarkdownPipelineBuilder

            $builder = [Markdig.MarkdownExtensions]::Configure($builder, 'advanced+yaml')
            $builder.PreciseSourceLocation = $true
            if ($TrackTrivia) {
                $builder = [Markdig.MarkdownExtensions]::EnableTrackTrivia($builder)
            }
            [Markdig.Syntax.MarkdownDocument]$document = [Markdig.Parsers.MarkdownParser]::Parse(
                $content ,
                $builder.Build()
            )

            $PSCmdlet.WriteObject($document, $false)
        }
        catch {
            $PSCmdlet.ThrowTerminatingError($_)
        }
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\private\Markdown\Import-Markdown.ps1 52
#region source\stitch\private\Markdown\New-MarkdownElement.ps1 1
function New-MarkdownElement {
    [CmdletBinding(
        ConfirmImpact = 'Low'
    )]
    param(
        # Text to parse into Markdown Element(s)
        [Parameter(
            ValueFromPipeline
        )]
        [string[]]$InputObject
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $collect = @()
    } process {
        $collect += $InputObject
    } end {
        [Markdig.Markdown]::Parse(
            ($collect -join [System.Environment]::NewLine) ,
            $true
        ) | Write-Output -NoEnumerate
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\private\Markdown\New-MarkdownElement.ps1 24
#region source\stitch\private\Markdown\Write-MarkdownDocument.ps1 1
function Write-MarkdownDocument {
    [CmdletBinding()]
    param(
        [Parameter(
            ValueFromPipeline
        )]
        [Markdig.Syntax.MarkdownObject]$InputObject
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        $sw = [System.IO.StringWriter]::new()
        $rr = [Markdig.Renderers.Roundtrip.RoundtripRenderer]::new($sw)

        $rr.Write($InputObject)
        $sw.ToString() | Write-Output
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\private\Markdown\Write-MarkdownDocument.ps1 22
#region source\stitch\private\SourceInfo\Get-SourceItemInfo.ps1 4
function Get-SourceItemInfo {
    [CmdletBinding()]
    param(
        # The directory to look in for source files
        [Parameter(
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [ValidateNotNullOrEmpty()]
        [string[]]$Path,

        # The root directory of the source item, using the convention of a
        # source folder with one or more module folders in it.
        # Should be the Module's Source folder of your project
        [Parameter(
            Position = 0
        )]
        [string]$Root,

        # Path to the source type map
        [Parameter(
        )]
        [string]$TypeMap

    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $POWERSHELL_FILETYPES = @(
            '.ps1',
            '.psm1'
        )
        $DATA_FILETYPES = @(
            '.psd1'
        )
        try {
            if ($PSBoundParameters.ContainsKey('TypeMap')) {
                $map = Get-SourceTypeMap -Path $TypeMap
            } else {
                # try to load defaults
                $map = Get-SourceTypeMap
            }
        }
        catch {
            throw "Could not find map for source types`n$_"
        }
    }
    process {
        foreach ($p in $Path) {
            Write-Debug "Processing $p"

            $fileTypes = $map.FileTypes
            try {
                $fileItem = Get-Item $p -ErrorAction Stop
            } catch {
                throw "Could not read $p`n$_"
            }
            if ($fileTypes.Keys -contains $fileItem.Extension) {
                $fileType = $fileTypes[$fileItem.Extension]
                Write-Debug " - $($file.Name) is a $fileType item"
            } else {
                Write-Verbose "Not adding $($fileItem.Name) because it is a $($fileItem.Extension) file"
                continue
            }
            $sourceObject = @{
                PSTypeName   = 'Stitch.SourceItemInfo'
                Path         = $fileItem.FullName
                BaseName     = $fileItem.BaseName
                FileName     = $fileItem.Name
                Name         = $fileItem.BaseName
                FileType     = $fileType
                Ast          = ''
                Directory    = ''
                Module       = ''
                Type         = ''
                Component    = ''
                Visibility   = ''
                Verb         = ''
                Noun         = ''
            }


            if ($POWERSHELL_FILETYPES -contains $fileItem.Extension) {
                try {
                    Write-Debug " - Parsing powershell"
                    $ast = [Parser]::ParseFile($fileItem.FullName, [ref]$null, [ref]$null)
                    if ($null -ne $ast) {
                        $sourceObject.Ast = $ast
                    }
                }
                catch {
                    Write-Warning "Could not parse source item $($fileItem.FullName)`n$_"
                }
            } elseif ($DATA_FILETYPES -contains $fileItem.Extension) {
                switch -Regex ($fileItem.Extension) {
                    '^\.psd1$' {

                        try {
                            $sourceObject['Data'] = Import-Psd $fileItem.FullName -Unsafe
                        }
                        catch {
                            Write-Warning "Could not import data from $($fileItem.FullName)`n$_"
                        }
                    }
                }
            }


            if ([string]::IsNullorEmpty($Root)) {
                Write-Debug " - No Root path given. Attempting to resolve from project root"
                $projectRoot = Resolve-ProjectRoot
                Write-Debug " - Project root is : $projectRoot"
                $relativeToProject = [System.IO.Path]::GetRelativePath($projectRoot, $fileItem.FullName)
                $projectPathParts = $relativeToProject -split [regex]::Escape([System.IO.Path]::DirectorySeparatorChar)
                $rootName = $projectPathParts[0]
                Write-Debug " - Guessing $rootName is the Source directory"
                $Root = (Join-Path $projectRoot $rootName)
                Write-Debug " - Setting Root to $Root"

            }

            if ([string]::IsNullorEmpty($Root)) {
                throw "Could not determine the Root directory for SourceItems"
            }

            Write-Debug "Getting relative path from root '$Root'"
            $adjustedPath = [System.IO.Path]::GetRelativePath($Root, $fileItem.FullName)
            Write-Debug " - '$($fileItem.FullName)' adjusted path is '$adjustedPath'"
            $sourceObject['ProjectPath'] = $adjustedPath

            $pathItems = [System.Collections.ArrayList]@(
                $adjustedPath -split [regex]::Escape([System.IO.Path]::DirectorySeparatorChar)
            )

            Write-Debug "Matching Path settings in sourcetypes"
            $levels = $map.Path
            :level foreach ($level in $levels) {
                $pathItemIndex = $levels.IndexOf($level)
                if ($pathItemsIndex -ge $pathItems.Count) {
                    Write-Debug " - Index is $pathItemsIndex. No more path items"
                    break level
                }
                $pathField = $pathItems[$pathItemIndex]
                if ($level -is [String]) {
                    Write-Debug " - level $pathItemIndex is $level"
                    $sourceObject[$level] = $pathField
                    Write-Debug " - $level => $pathField"
                    continue level
                } elseif ($level -is [hashtable]) {
                    Write-Debug " - level $pathItemIndex is a hashtable"
                    foreach ($pattern in $level.Keys) {
                        Write-Debug " - testing if $pathField matches $pattern"
                        if ($pathField -match $pattern) {
                            foreach ($field in ($level[$pattern]).Keys) {
                                $sourceObject[$field] = $level[$pattern][$field]
                            }
                        }
                    }
                    continue level
                }
            }

            Write-Debug "Matching Name settings in sourcetypes"
            foreach ($namePattern in $map.Name.Keys) {
                if ($fileItem.Name -match $namePattern) {
                    Write-Debug " - $($fileItem.Name) matches $namePattern"
                    $nameMaps = $map.Name[$namePattern]
                    $nameMatches = $Matches
                    foreach ($nameMap in $nameMaps.GetEnumerator()) {
                        Write-Debug " - Name map $($nameMap.Name) => $($nameMap.Value)"
                        if ($nameMap.Value -match '^Matches\.(\d+)') {
                            $matchNumber = [int]$Matches.1
                            Write-Debug " - Match number: $($nameMap.Name) => $($nameMatches[$matchNumber])"
                            $sourceObject[$nameMap.Name] = $nameMatches[$matchNumber]
                        } elseif ($nameMap.Value -match '^Matches\.(\w+)') {
                            $matchWord = $Matches.1
                            Write-Debug " - Match word: $($nameMap.Name) => $($nameMatches[$matchWord])"
                            $sourceObject[$nameMap.Name] = $nameMatches[$matchWord]
                        } else {
                            Write-Debug " - $($nameMap.Name) => $($nameMap.Value)"
                            $sourceObject[$nameMap.Name] = $nameMap.Value
                        }
                    }
                }
            }

            #! special case: Manifest file
            if ($fileItem.Extension -like '.psd1') {
                if ($sourceObject.Data.ContainsKey('GUID')) {
                    $sourceObject['FileType'] = 'PowerShell Module Manifest'
                    $sourceObject['Type'] = 'manifest'
                    $sourceObject['Visibility'] = 'public'
                }
            }
            [PSCustomObject]$sourceObject | Write-Output
        } # end foreach
    } # end process block
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\private\SourceInfo\Get-SourceItemInfo.ps1 205
#region source\stitch\private\SourceInfo\Get-TestItemInfo.ps1 1
function Get-TestItemInfo {
    [CmdletBinding()]
    param(
        # The directory to look in for source files
        [Parameter(
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [ValidateNotNullOrEmpty()]
        [string[]]$Path,

        # The root directory to use for test properties
        [Parameter(
        )]
        [string]$Root,

        # Optionally run the testes
        [Parameter(
        )]
        [switch]$RunTest

    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        foreach ($p in $Path) {
            Write-Debug "Processing $p"
                #-------------------------------------------------------------------------------
                #region File selection

                $fileItem = Get-Item $p -ErrorAction Stop
                #TODO: Are there other extensions we should look for ?
                if ($fileItem.Extension -notlike '.ps1') {
                    Write-Verbose "Not adding $($fileItem.Name) because it is not a .ps1 file"
                    continue
                } else {
                    Write-Debug "$($fileItem.Name) is a test item"
                }
                #endregion File selection
                #-------------------------------------------------------------------------------

                #-------------------------------------------------------------------------------
                #region Object creation
                Write-Debug " Creating item $($fileItem.BaseName) from $($fileItem.FullName)"
                Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
                $pesterConfig = New-PesterConfiguration
                $pesterConfig.Run.PassThru = $true
                $pesterConfig.Run.SkipRun = (-not ($RunTest))
                try {
                    $pesterContainer = New-PesterContainer -Path:$p
                    $pesterConfig.Run.Container = $pesterContainer
                    $testResult = Invoke-Pester -Configuration $pesterConfig
                    Write-Debug "Root is $Root"
                } catch {
                    throw "Could not load test item $Path`n$_ "
                }
            }
                Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
            }
            end {
                $testResult
                Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
            }

        }
#endregion source\stitch\private\SourceInfo\Get-TestItemInfo.ps1 68
#region source\stitch\private\Template\Get-StitchTemplateMetadata.ps1 1
function Get-StitchTemplateMetadata {
    [CmdletBinding()]
    param(
        # Specifies a path to one or more locations.
        [Parameter(
        Position = 0,
        ValueFromPipeline,
        ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string[]]$Path
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $content = ($template | Get-Content -Raw)
        $null = $content -match '(?sm)---(.*?)---'
        if ($Matches.Count -gt 0) {
            Write-Debug " - YAML header info found $($Matches.1)"
            $Matches.1 | ConvertFrom-Yaml | Write-Output
        }

        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\private\Template\Get-StitchTemplateMetadata.ps1 30
#region source\stitch\private\Template\Invoke-StitchTemplate.ps1 1
function Invoke-StitchTemplate {
    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact = 'Medium'
    )]
    param(

        # Specifies a path to the template source
        [Parameter(
            Mandatory,
            Position = 1,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string]$Source,

        # The directory to place the new file in
        [Parameter()]
        [string]$Destination,

        # The name of target file
        [Parameter(
            ValueFromPipelineByPropertyName
        )]
        [string]$Name,

        # The target path to write the template output to
        [Parameter(
            Mandatory,
            Position = 0,
            ValueFromPipelineByPropertyName
        )]
        [string]$Target,

        # Binding data to be given to the template
        [Parameter(
            ValueFromPipelineByPropertyName
        )]
        [hashtable]$Data,

        # Overwrite the Target with the output
        [Parameter(
        )]
        [switch]$Force,

        # Return the path to the generated file
        [Parameter(
        )]
        [switch]$PassThru
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"

        if (-not ([string]::IsNullorEmpty($Source))) {
            if (-not (Test-Path $Source)) {
                throw "Template file $Source not found"
            }

            try {
                if ([string]::IsNullorEmpty($Data)) {
                    $Data = @{}
                }

                $Data['Name'] = $Name
                # Templates can use this to import/include other templates
                $Data['TemplatePath'] = $Source | Split-Path -Parent


                $templateOptions = @{
                    Path = $Source
                }


                $templateOptions['Binding'] = $Data
                $templateOptions['Safe'] = $true

                Write-Debug "Converting template $Name with options"
                Write-Debug "Output of template to $Target"
                foreach ($option in $templateOptions.Keys) {
                    Write-Debug " - $option => $($templateOptions[$option])"
                }
                if (-not ([string]::IsNullorEmpty($templateOptions.Binding))) {
                    Write-Debug " - Bindings:"
                    foreach ($key in $templateOptions.Binding.Keys) {
                        Write-Debug " - $key => $($templateOptions.Binding[$key])"
                    }
                }

                $verboseFile = [System.IO.Path]::GetTempFileName()
                <#
                EPS builds the templates using StringBuilder, and then "executes" them in a separate powershell
                instance. Because of that, some errors and exceptions dont show up, you just get no output. To
                get the actual error, you need to see what the error of the scriptblock is. It looks like there is
                an update on the [github repo](https://github.com/straightdave/eps) but it is not the released
                version
 
                ! So to confirm that the template functions correctly, check for content first
                #>

                $content = Invoke-EpsTemplate @templateOptions -Verbose 4>$verboseFile
                #! Check this here and use it after we are out of the try block
                $contentExists = (-not([string]::IsNullorEmpty($content)))
            } catch {
                $PSCmdlet.ThrowTerminatingError($_)
            }

            if ($contentExists) {
                $overwrite = $false
                if (Test-Path $Target) {
                    if (-not ($Force)) {
                        $writeErrorSplat = @{
                            Message = "$Target already exists. Use -Force to overwrite"
                            Category = 'ResourceExists'
                            CategoryTargetName = $Target
                        }

                        Write-Error @writeErrorSplat
                    } else {
                        $overwrite = $true
                    }
                }

                if ($overwrite) {
                    Write-Debug "It does exist"
                    $operation = 'Overwrite file'
                } else {
                    Write-Debug 'It does not exist yet'
                    $operation = 'Write file'
                }

                if ($PSCmdlet.ShouldProcess($Target, $operation)) {
                    try {
                        $targetDir = $Target | Split-Path -Parent
                        if (-not (Test-Path $targetDir)) {
                            mkdir $targetDir -Force
                        }
                        $content | Set-Content $Target
                        if ($PassThru) {
                            $Target | Write-Output
                        }
                    } catch {
                        throw "Could not write template content to $Target`n$_"
                    }
                }
            } else {
                #-------------------------------------------------------------------------------
                #region Get template error
                Write-Debug "No content. Getting inner error"
                $verboseOutput = [System.Collections.ArrayList]@(Get-Content $verboseFile)
                #! Replace the first and last lines with braces to make it a scriptblock so we can execute the inner content
                $null = $verboseOutput.RemoveAt(0)
                $null = $verboseOutput.RemoveAt($verboseOutput.Count - 1)

                $null = $verboseOutput.Insert( 0 , 'try {')
                $verboseOutput += @(
                    '} catch {',
                    'throw $_',
                    '}'
                )
                $stringBuilderScript = [scriptblock]::Create(($verboseOutput | Out-String))
                try {
                    Invoke-Command -ScriptBlock $stringBuilderScript
                } catch {
                    throw $_
                }
                #endregion Get template error
                #-------------------------------------------------------------------------------
            }
        } else {
            throw "No Source given to process"
        }

        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\private\Template\Invoke-StitchTemplate.ps1 181
#endregion Private functions
#===============================================================================

#===============================================================================
#region Public functions
#region source\stitch\public\Changelog\Add-ChangelogEntry.ps1 2
function Add-ChangelogEntry {
    <#
    .SYNOPSIS
        Add an entry to the changelog
    #>

    [CmdletBinding()]
    param(
        # The commit to add
        [Parameter(
            Position = 1,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [PSTypeName('Git.ConventionalCommitInfo')][Object]$Commit,

        # Specifies a path to the changelog file
        [Parameter(
            Position = 0
        )]
        [Alias('PSPath')]
        [string]$Path,

        # The release to add the entry to
        [Parameter(
            Position = 2
        )]
        [string]$Release = 'unreleased'
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        enum DocumentState {
            NONE = 0
            RELEASE = 1
            GROUP = 2
        }
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $group = $Commit | Resolve-ChangelogGroup
        Write-Debug "Commit $($Commit.MessageShort) resolves to group $($group.DisplayName)"

        if (Test-Path $Path) {
            Write-Debug "Now parsing $Path"
            [Markdig.Syntax.MarkdownDocument]$doc = $Path | Import-Markdown -TrackTrivia
            $state = [DocumentState]::NONE
            $tokenCount = 0
            foreach ($token in $doc) {
                Write-Debug "--- $state : Line $($token.Line) $($token.GetType()) Index $($doc.IndexOf($token))"
                switch ($token.GetType()) {
                    'Markdig.Syntax.HeadingBlock' {
                        switch ($token.Level) {
                            2 {
                                Write-Debug " - Is a level 2 heading"
                                $text = $token | Format-HeadingText -NoLink
                                if ($text -match [regex]::Escape($Release)) {
                                    Write-Debug " - *** Heading '$text' matches $Release ***"
                                    $state = [DocumentState]::RELEASE
                                } else {
                                    Write-Debug " - $text did not match"
                                }
                                continue
                            }
                            3 {
                                Write-Debug " - Is a level 3 heading"
                                if ($state -eq [DocumentState]::RELEASE) {
                                    $text = $token | Format-HeadingText -NoLink
                                    if ($text -like $group.DisplayName) {
                                        Write-Debug " - *** Heading '$text' matches group ***"
                                        $state = [DocumentState]::GROUP
                                    }
                                } else {
                                    Write-Debug " - Not in release"
                                }
                                continue
                            }
                            Default {}
                        }
                        continue
                    }
                    'Markdig.Syntax.ListBlock' {
                        if ($state -eq [DocumentState]::GROUP) {
                            Write-Debug "Listblock while GROUP is set"
                            $text = $Commit | Format-ChangelogEntry
                            Write-Debug "Wanting to add '$text' to the list"
                            Write-Debug "$($token.Count) items in the list"
                            # $conversion = $text | ConvertFrom-Markdown | Select-Object -ExpandProperty Tokens |
                            # Select-Object -First 1
                            $text = "$([System.Environment]::NewLine)$text"
                            $entry = [Markdig.Markdown]::Parse($text, $true)


                            Write-Debug "The entry we want to add is a $($entry.GetType()) at $tokenCount"
                            try {
                                $doc.Insert($doc.IndexOf($token), $entry)

                            }
                            catch {
                                $PSCmdlet.ThrowTerminatingError($_)
                            } finally {
                                $state = [DocumentState]::NONE
                            }
                        }
                        continue
                    }
                    'Markdig.Syntax.LinkReferenceDefinitionGroup' {
                        $doc.RemoveAt($doc.IndexOf($token))
                    }
                }
                $tokenCount++
            }
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $doc | Write-MarkdownDocument | Out-File $Path
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Changelog\Add-ChangelogEntry.ps1 119
#region source\stitch\public\Changelog\ConvertFrom-Changelog.ps1 2
function ConvertFrom-Changelog {
    <#
    .SYNOPSIS
        Convert a Changelog file into a PSObject
    #>

    [CmdletBinding()]
    param(
        # Specifies a path to one or more locations.
        [Parameter(
            Mandatory,
            Position = 0,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [ValidateNotNullOrEmpty()]
        [string[]]$Path,

        # Optionally return a hashtable instead of an object
        [Parameter(
        )]
        [switch]$AsHashTable
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $changelogObject = @{
            Releases = [System.Collections.ArrayList]@()
        }
    }
    process {
        foreach ($file in $Path) {
            if (Test-Path $file) {
                try {
                    Write-Debug "Importing markdown document $file"
                    $doc = Get-Item $file | Import-Markdown
                } catch {
                    throw "Error parsing markdown`n$_"
                }
            } else {
                throw "$file is not a valid path"
            }
            Write-Debug "Parsing tokens in $file"
            foreach ($token in $doc) {
                switch ($token) {
                    { $_ -is [Markdig.Syntax.HeadingBlock] } {
                        $text = $token | Format-HeadingText
                        switch ($token.Level) {
                            <#
                if this is a level 2 heading then it is a new release
                every token after this one should be added to the release
                and the group should be added to the changelog after it has been completely
                filled out
                #>

                            2 {
                                Write-Debug "at Line $($token.Line) Found new release heading '$text'"
                                if ($null -ne $thisRelease) {
                                    Write-Debug ' - Adding previous group to changelog'
                                    if ($AsHashTable) {
                                        $null = $changelogObject.Releases.Add($thisRelease)
                                    } else {
                                        $null = $changelogObject.Releases.Add([PSCustomObject]$thisRelease)
                                    }

                                    Remove-Variable release, group -ErrorAction SilentlyContinue
                                }
                                $thisRelease = @{
                                    Groups   = [System.Collections.ArrayList]@()
                                }
                                if (-not($AsHashTable)) {
                                    $thisRelease['PSTypeName'] = 'Changelog.Release'
                                }

                                # unreleased
                                if ($text -match '^\[?unreleased\]? - (.*)?') {
                                    Write-Debug '- matches unreleased'
                                    $thisRelease['Version'] = 'unreleased'
                                    $thisRelease['Name'] = 'unreleased'
                                    $thisRelease['Type'] = 'Unreleased'
                                    if ($null -ne $Matches.1) {
                                        $thisRelease['Timestamp'] = (Get-Date $Matches.1)
                                    } else {
                                        $thisRelease['Timestamp'] = (Get-Date -Format 'yyyy-MM-dd')
                                    }
                                    # version, link and date
                                    # [1.0.1](https://github.com/user/repo/compare/vprev..vcur) 1986-02-25
                                } elseif ($text -match '^\[(?<ver>[0-9\.]+)\]\((?<link>[^\)]+)\)\s*-?\s*(?<dt>\d\d\d\d-\d\d-\d\d)?') {
                                    Write-Debug '- matches version,link and date'
                                    if ($null -ne $Matches.ver) {
                                        $thisRelease['Type'] = 'Release'
                                        $thisRelease['Version'] = $Matches.ver
                                        $thisRelease['Name'] = $Matches.ver
                                        if ($null -ne $Matches.dt) {
                                            $thisRelease['Link'] = $Matches.link
                                        }
                                        if ($null -ne $Matches.dt) {
                                            $thisRelease['Timestamp'] = $Matches.dt
                                        }
                                    }
                                    # version and date
                                    # [1.0.1] 1986-02-25
                                } elseif ($text -match '^\[(?<ver>[0-9\.]+)\]\s*-?\s*(?<dt>\d\d\d\d-\d\d-\d\d)?') {
                                    Write-Debug '- matches version and date'
                                    if ($null -ne $Matches.ver) {
                                        $thisRelease['Type'] = 'Release'
                                        $thisRelease['Version'] = $Matches.ver
                                        $thisRelease['Name'] = $Matches.ver
                                        if ($null -ne $Matches.dt) {
                                            $thisRelease['Timestamp'] = $Matches.dt
                                        }
                                    }
                                }
                            }
                            3 {
                                if ($null -ne $group) {
                                    if ($AsHashTable) {
                                        $null = $thisRelease.Groups.Add($group)
                                    } else {
                                        $null = $thisRelease.Groups.Add([PSCustomObject]$group)
                                    }
                                    $group.Clear()
                                }
                                $group = @{
                                    Entries = [System.Collections.ArrayList]@()
                                }
                                $group['DisplayName'] = $text
                                $group['Name'] = $text
                                if (-not($AsHashTable)) {
                                    $group['PSTypeName'] = 'Changelog.Group'
                                }
                            }
                        }
                    }
                    { $_ -is [Markdig.Syntax.ListItemBlock] } {
                        Write-Debug " - list item block at line $($token.Line) column $($token.Column)"
                        # token is a collection of ListItems
                        foreach ($listItem in $token) {
                            Write-Debug " - list item at line $($listItem.Line) column $($listItem.Column)"
                            $text = $listItem.Inline.Content.ToString()
                            $null = $group.Entries.Add(
                                @{
                                    Title = $text
                                    Description = $text
                                }
                            )
                        }
                        continue
                    }
                }
            }
        }
        Write-Debug ' - adding last release to changelog'
        if ($AsHashTable) {
            $null = $changelogObject.Releases.Add($thisRelease)
        } else {
            $null = $changelogObject.Releases.Add([PSCustomObject]$thisRelease)
        }
    }
    end {
        if ($AsHashTable) {
            $changelogObject | Write-Output
        } else {
            $changelogObject['PSTypeName'] = 'ChangelogInfo'
            [PSCustomObject]$changelogObject | Write-Output
        }
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Changelog\ConvertFrom-Changelog.ps1 168
#region source\stitch\public\Changelog\ConvertTo-Changelog.ps1 2
function ConvertTo-Changelog {
    <#
    .SYNOPSIS
        Convert Git-History to a Changelog
    #>

    [CmdletBinding()]
    param(
        # A git history table to be converted
        [Parameter(
            ValueFromPipeline
        )]
        [hashtable]$History
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $config = Get-ChangelogConfig
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"

        Format-ChangelogHeader
        [System.Environment]::NewLine

        foreach ($releaseName in (($History.GetEnumerator() | Sort-Object { $_.Value.Timestamp } -Descending | Select-Object -ExpandProperty Name))) {
            $release = $History[$releaseName]
            $release | Format-ChangelogRelease
            [System.Environment]::NewLine

            foreach ($groupName in ($release.Groups.GetEnumerator() | Sort-Object { $_.Value.Sort } | Select-Object -ExpandProperty Name)) {
                if ($groupName -like 'omit') { continue }
                $group = $release.Groups[$groupName]
                $group | Format-ChangelogGroup
                [System.Environment]::NewLine

                foreach ($entry in $group.Entries) {
                    $entry | Format-ChangelogEntry
                }
                 [System.Environment]::NewLine
            }
        }

        [System.Environment]::NewLine

        Format-ChangelogFooter

        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Changelog\ConvertTo-Changelog.ps1 52
#region source\stitch\public\Changelog\Export-ReleaseNotes.ps1 5
function Export-ReleaseNotes {
    [SuppressMessage('PSUseSingularNouns', '', Justification = 'ReleaseNotes is a single document' )]
    [CmdletBinding()]
    param(
        # Specifies a path to the Changelog.md file
        [Parameter(
            Position = 2,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string]$Path = 'CHANGELOG.md',

        # The path to the destination file. Outputs to pipeline if not specified
        [Parameter(
            Position = 0
        )]
        [string]$Destination,

        # The release version to create a release from
        [Parameter(
        )]
        [string]$Release
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $changelogData = $null
        function outputItem {
            param(
                [Parameter(
                    Position = 1,
                    ValueFromPipeline
                )]
                [string]$Item,

                [Parameter(
                    Position = 0
                )]
                [bool]$toFile
            )
            if ($toFile) {
                $Destination | Add-Content $Item
            } else {
                $Item | Write-Output
            }
        }
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $writeToFile = $PSBoundParameters.ContainsKey('Destination')

        if (-not ([string]::IsNullorEmpty($Path))) {
            if (Test-Path $Path) {
                Write-Debug "Converting Changelog : $Path"
                $dpref = $DebugPreference
                $DebugPreference = 'SilentlyContinue'
                $changelogData = ($Path | ConvertFrom-Changelog)
                $DebugPreference = $dpref
                if ($null -ne $changelogData) {
                    Write-Debug "There are $($changelogData.Releases.Count) release sections"
                    :section foreach ($section in $changelogData.Releases ) {
                        Write-Debug "$($section.Type) Section: Version = $($section.Version) Timestamp = $($section.Timestamp)"
                        if ($section.Type -like 'Unreleased') {
                            continue section
                        }
                        if (-not ([string]::IsNullorEmpty($Release))) {
                            if ( [semver]::new($section.Version) -gt [semver]::new($Release)) {
                                continue section
                            }
                        }
                        #! we can use our Format to assemble the Timestamp, version, etc
                        #! the other items should already be in the format we want
                        $section | Format-ChangelogRelease | outputItem $writeToFile
                        foreach ($group in $section.Groups) {
                            #! no need to reformat it
                            $group | Format-ChangelogGroup | outputItem $writeToFile
                            foreach ($entry in $group.Entries) {
                                $entry | Format-ChangelogEntry | outputItem $writeToFile
                            }
                        }
                    }
                }
            }
        } else {
            throw "$Path is not a valid Path"
        }

        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Changelog\Export-ReleaseNotes.ps1 97
#region source\stitch\public\Changelog\Get-ChangelogConfig.ps1 2
function Get-ChangelogConfig {
    <#
    .SYNOPSIS
        Look for a psd1 configuration file in the local folder, the path specified, or the module folder
    #>

    [CmdletBinding()]
    param(
        # Specifies a path to one or more locations.
        [Parameter(
        Position = 0,
        ValueFromPipeline,
        ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string]$Path
    )
    begin {
        $defaultConfigFile = '.changelog.config.psd1'
    }
    process {
        if (-not($PSBoundParameters.ContainsKey('Path'))) {
            #! if not specified, look in the local directory for the config file
            $Path = Get-Location
        }

        Write-Debug "Path is set as $Path"
        if (Test-Path $Path) {
            $pathItem = Get-Item $Path
            if ($pathItem.PSIsContainer) {
                Write-Debug "Path is a directory. Looking for $defaultConfigFile"
                $possiblePath = (Join-Path $pathItem $defaultConfigFile)
                # look for the file in the directory
                if (Test-Path $possiblePath) {
                    Write-Debug " - Found"
                    $configFile = Get-Item $possiblePath
                }
            } else {
                $configFile = $pathItem
            }

        } else {
            $configFile = Get-Item (Join-Path $ExecutionContext.SessionState.Module.ModuleBase $defaultConfigFile)
        }
        Write-Verbose "Loading configuration from $($configFile.FullName)"
        $config = Import-PowerShellDataFile $configFile.FullName

    }
    end {
        $config
    }
}
#endregion source\stitch\public\Changelog\Get-ChangelogConfig.ps1 52
#region source\stitch\public\Changelog\Set-ChangelogRelease.ps1 2
function Set-ChangelogRelease {
    <#
    .SYNOPSIS
        Create a new release section in the Changelog based on the changes in 'Unreleased' and creates a new blank
        'Unreleased' section
    #>

    [Alias('Update-Changelog')]
    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact = 'medium'
    )]
    param(
        # Specifies a path to the changelog file
        [Parameter(
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string]$Path,

        # The unreleased section will be moved to this version
        [Parameter(
        )]
        [string]$Release,

        # The date of the release
        [Parameter(
        )]
        [datetime]$releaseDate,

        # Skip checking the current git tag information
        [Parameter(
        )]
        [switch]$SkipGitTag
    )
    begin {
        Write-Verbose "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $config = Get-ChangelogConfig
        if (-not($SkipGitTag)) {
            if ($PSBoundParameters.ContainsKey('Release')) {
                $tag = Get-GitTag -Name $Release -ErrorAction SilentlyContinue
            } else {
                $tag = Get-GitTag | Where-Object {
                    $_.Name -match $config.TagPattern
                } |  Select-Object -First 1 -ErrorAction SilentlyContinue
            }

            if ($null -ne $tag) {
                $releaseDate = $tag.Target.Author.When.UtcDateTime
                if ($null -ne $Release) {
                    $null = $tag.FriendlyName -match $config.TagPattern
                    if ($Matches.Count -gt 0) {
                        $Release = $Matches.1
                    }
                }
            } else {
                $PSCmdlet.WriteError("Could not find tag $Release")
            }
        }
        if ([string]::IsNullorEmpty($Release)) {
            throw "No Release version could be found"
        }

        if ([string]::IsNullorEmpty($ReleaseDate)) {
            $PSCmdlet.WriteError("No release date was found for release $Release")
        }
    }
    process {
        Write-Verbose "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        if (Test-Path $Path) {
            Write-Verbose "Setting up temp document"
            $tempFile = [System.IO.Path]::GetTempFileName()
            Get-Content $Path -Raw | Set-Content $tempFile

            Write-Verbose "Now parsing document"
            [Markdig.Syntax.MarkdownDocument]$doc = $tempFile | Import-Markdown -TrackTrivia

            $currentVersionHeading = $doc | Get-MarkdownHeading | Where-Object {
                ($_ | Format-HeadingText -NoLink) -match $config.CurrentVersion
            }
            Write-Verbose "Found $($currentVersionHeading.Count) current version headings"

            if ($null -ne $currentVersionHeading) {
                $afterCurrentHeading = ($doc.IndexOf($currentVersionHeading) + 1)
                $releaseData = @{
                    Name = $Release
                    TimeStamp = $releaseDate
                }
                $newHeading = [Markdig.Markdown]::Parse(
                    ($releaseData | Format-ChangelogRelease),
                    $true
                )
                if ($null -ne $newHeading) {
                    $newText =$newHeading | Format-HeadingText -NoLink
                    Write-Verbose "New Heading is $newText"
                    [ref]$doc | Add-MarkdownElement $newHeading -Index $afterCurrentHeading
                    [ref]$doc | Add-MarkdownElement ([Markdig.Syntax.BlankLineBlock]::new()) -Index $afterCurrentHeading
                }
            }

            $linkRefs = $doc | Get-MarkdownElement LinkReferenceDefinitionGroup | Select-Object -First 1
            if ($null -ne $linkRefs) {
                $doc.RemoveAt($doc.IndexOf($linkRefs))
            }
            try {
                $doc | Write-MarkdownDocument | Out-File $tempFile
                #! -Force required to overwrite our file
                $tempFile | Move-Item -Destination $Path -Force
            } catch {
                $PSCmdlet.ThrowTerminatingError($_)
            }
        }
        Write-Verbose "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Verbose "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Changelog\Set-ChangelogRelease.ps1 119
#region source\stitch\public\Configuration\Convert-ConfigurationFile.ps1 2
function Convert-ConfigurationFile {
    <#
    .SYNOPSIS
        Convert a configuration file into a powershell hashtable. Can be psd1, yaml, or json
    #>

    [CmdletBinding()]
    param(
        # Specifies a path to one or more configuration files.
        [Parameter(
        Position = 0,
        ValueFromPipeline,
        ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string[]]$Path
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        Write-Debug "Getting ready to convert configuration file $Path"
        if (Test-Path $Path) {
            Write-Debug ' - File exists'
            $pathItem = Get-Item $Path
            if ($pathItem.PSISContainer) {
                Get-ChildItem -Path $Path -Recurse | Convert-ConfigurationFile
            } else {
                switch -Regex ($pathItem.Extension) {
                    '\.psd1' {
                        #! Note we use the 'Unsafe' parameter so we can have scriptblocks and
                        #! variables in our psd
                        Write-Debug ' - Importing PSD'
                        $configOptions = (Import-Psd -Path $pathItem -Unsafe) | Write-Output
                    }
                    '\.y(a)?ml' {
                        Write-Debug ' - Importing YAML'
                        $configOptions = (Get-Content $pathItem | ConvertFrom-Yaml -Ordered) | Write-Output
                    }
                    '\.json(c)?' {
                        Write-Debug ' - Importing JSON'
                        $configOptions = (Get-Content $pathItem | ConvertFrom-Json -Depth 16) | Write-Output
                    }
                    default {
                        Write-Warning "Could not determine the type for $($pathItem.FullName)"
                    }
                }
            }
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Configuration\Convert-ConfigurationFile.ps1 56
#region source\stitch\public\Configuration\Get-BuildConfiguration.ps1 3
function Get-BuildConfiguration {
    <#
    .SYNOPSIS
        Gather information about the project for use in tasks
    .DESCRIPTION
        `Get-BuildConfiguration` collects information about paths, source items, versions and modules that it finds
        in -Path. Configuration information can be added/updated using configuration files.
    .EXAMPLE
        Get-BuildConfiguration . -ConfigurationFiles ./.build/config
        gci .build\config | Get-BuildConfiguration .
    #>

    [OutputType([System.Collections.Specialized.OrderedDictionary])]
    [CmdletBinding()]
    param(
        # Specifies a path to the folder to build the configuration for
        [Parameter(
            Position = 0,
            ValueFromPipelineByPropertyName
        )]
        [string]$Path = (Get-Location),

        # Path to the build configuration file
        [Parameter(
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string[]]$ConfigurationFiles,

        # Default Source directory
        [Parameter(
        )]
        [string]$Source,

        # Default Tests directory
        [Parameter(
        )]
        [string]$Tests,

        # Default Staging directory
        [Parameter(
        )]
        [string]$Staging,

        # Default Artifact directory
        [Parameter(
        )]
        [string]$Artifact,

        # Default Docs directory
        [Parameter(
        )]
        [string]$Docs
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"

        # The info table holds all of the gathered project information, which will ultimately be returned to the
        # caller
        $info = [ordered]@{
            Project = @{}
        }

        #-------------------------------------------------------------------------------
        #region Set defaults

        <#
         !used throughout to set "project locations"
         which is why we don't just add it directly to $info
        #>

        $defaultLocations = @{
            Source   = "source"
            Tests    = 'tests'
            Staging  = 'stage'
            Artifact = 'out'
            Docs     = 'docs'
        }

        # Add them as top level keys
        $defaultLocations.Keys | ForEach-Object { $info[$_] = '' }

        #endregion Set defaults
        #-------------------------------------------------------------------------------

        #-------------------------------------------------------------------------------
        #region Normalize paths

        Write-Debug ( @(
                "Paths used to build configuration:",
                "Path : $Path",
                "Source : $Source",
                "Staging : $Staging",
                "Artifact : $Artifact",
                "Tests : $Tests",
                "Docs : $Docs") -join "`n")

        $possibleRoot = $PSCmdlet.GetVariableValue('BuildRoot')
        if ($null -eq $possibleRoot) {
            Write-Debug "`$BuildRoot not found, using current location"
            $possibleRoot = (Get-Location)
        }

        foreach ($location in $defaultLocations.Keys) {
            Write-Debug "Setting the $location path"
            <#
                     The paths to the individual locations are vital to the correct operation of
                     the build.
                     Each variable is checked to see if it exists as a parameter, and then in the
                      caller scope (set via the script that called this function).
                      Finally, we test to see if the "default" is true, and add it
                    #>


            if ($PSBoundParameters.ContainsKey($location)) {
                $possibleLocation = $PSBoundParameters[$location]
            } elseif ($PSCmdlet.GetVariableValue($location)) {
                $possibleLocation = $PSCmdlet.GetVariableValue($location)
            } else {
                $possibleLocation = $defaultLocations[$location]
            }

            if ($null -ne $possibleLocation) {
                if (-not([System.IO.Path]::IsPathFullyQualified($possibleLocation))) {
                    $possibleLocation = (Join-Path $possibleRoot $possibleLocation)
                }

                if (-not(Test-Path $possibleLocation)) {
                    Write-Warning "$possibleLocation set as `$$location, but path does not exist"
                }
                #? Not sure what the right action is here. I could fail the function
                #? because I can't find the path... for now, I will just leave the
                #? unresolved string there because it must have been for a reason?
                Write-Debug " - $possibleLocation"
                $info[$location] = $possibleLocation
            }
        }
        #endregion Normalize paths
        #-------------------------------------------------------------------------------

        #-------------------------------------------------------------------------------
        #region Feature flags

        $flags = Get-FeatureFlag
        if ($null -ne $flags) {
            $info['Flags'] = $flags
        } else {
            Write-Debug "No feature flags were found"
        }

        #endregion Feature flags
        #-------------------------------------------------------------------------------

        #-------------------------------------------------------------------------------
        #region Version info
        $versionInfo = Get-ProjectVersionInfo

        if ($null -ne $versionInfo) {
            Write-Debug "Setting 'Version' key with version info"
            $info.Project['Version'] = $versionInfo
        } else {
            Write-Debug 'No version information found in project'
        }
        #endregion Version info
        #-------------------------------------------------------------------------------
    }
    process {
        #-------------------------------------------------------------------------------
        #region Configuration files

        foreach ($f in $ConfigurationFiles) {
            Write-Debug "Merging $f into BuildInfo"
            if (Test-Path $f) {
                $f | Merge-BuildConfiguration -Object ([ref]$info)
            }
        }

        #endregion Configuration files
        #-------------------------------------------------------------------------------
    }
    end {
        try {
            Write-Debug 'Resolving project root'
            $resolveRootOptions = @{
                Path     = $Path
                Source   = $Source
                Tests    = $Tests
                Staging  = $Staging
                Artifact = $Artifact
                Docs     = $Docs

            }
            $root = (Get-Item (Resolve-ProjectRoot @resolveRootOptions -ErrorAction SilentlyContinue))

        } catch {
            Write-Warning "Could not find Project Root`n$_"
        }

        if ($null -ne $root) {
            Write-Debug ' - root found:'
            Write-Debug " - Path is : $($root.FullName)"
            Write-Debug " - Name is : $($root.BaseName)"

            $info['Project'] = @{
                Path = $root.FullName
                Name = $root.BaseName
            }
        } else {
            Write-Debug " - Project root was not found. 'Path' and 'Name' will be empty"
            $info['Project'] = @{
                Path = ''
                Name = ''
            }
        }

        $info['Modules'] = @{}

        Write-Debug " Loading modules from $($info.Source)"
        foreach ($item in (Get-ModuleItem $info.Source)) {
            Write-Debug " Adding $($item.Name) to the collection"
            #! Get the names of the paths to process from failsafe_defaults, but the
            #! values come from the info table
            Write-Debug " - Adding field 'Paths' to module $($item.Name)"
            $item | Add-Member -NotePropertyName Paths -NotePropertyValue ($defaultLocations.Keys)
            foreach ($location in $defaultLocations.Keys) {
                $moduleLocation = (Join-Path $info[$location] $item.Name)
                Write-Debug " - Adding $location Path : $moduleLocation"
                $item | Add-Member -NotePropertyName $location -NotePropertyValue $moduleLocation
            }
            $info.Modules[$item.Name] = $item
        }

        <#------------------------------------------------------------------
              Now, configure the directories for each module. If a module is
              a Nested Module of another, then the staging folder should be:
              $Staging/RootModuleName/NestedModuleName
            ------------------------------------------------------------------#>


        Write-Debug "$('-' * 80)`n --- Getting NestedModules"
        foreach ($key in $info.Modules.Keys) {
            $currentModule = $info.Modules[$key]
            if ($null -ne $currentModule.NestedModules) {
                foreach ($nest in $currentModule.NestedModules) {
                    if ($nest -is [string]) {
                        $nestedModule = $nest
                    } elseif ($nest -is [hashtable]) {
                        $nestedModule = $nest.ModuleName
                    }
                    Write-Debug " Nested module: $nestedModule"
                    $found = ''
                    switch -Regex ($nestedModule) {
                        # path\to\ModuleName.psm1
                        # path/to/ModuleName.psm1
                        '[\\/]?(?<fname>)\.psm1$' {
                            Write-Debug " - Found path to module file $($Matches.fname)"
                            $found = $Matches.fname
                            continue
                        }
                        # path\to\ModuleName
                        # path/to/ModuleName
                        '(\w+[\\/])*(?<lword>\w+)$' {
                            Write-Debug " - Found path to directory $($Matches.lword)"
                            $found = $Matches.lword
                            continue
                        }
                        Default {
                            Write-Debug ' - Does not match a pattern'
                            $found = $nestedModule
                        }
                    }
                    if ($info.Modules.Keys -contains $found) {
                        Write-Debug " Adding $($currentModule.Name) as parent of $found"
                        $info.Modules[$found] | Add-Member -NotePropertyName 'Parent' -NotePropertyValue $currentModule.Name
                    } else {
                        Write-Debug " $found not found in project's modules`n$($info.Modules.Keys -join "`n - ") "
                    }
                }
            }
        }
        Write-Debug "Completed building configuration settings"
        $info
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Configuration\Get-BuildConfiguration.ps1 284
#region source\stitch\public\Configuration\Get-TaskConfiguration.ps1 3
function Get-TaskConfiguration {
    [CmdletBinding()]
    param(
        # The task object
        [Parameter(
            Position = 1,
            ValueFromPipeline
        )]
        [psobject]$Task,

        [Parameter(
            Position = 0
        )]
        [string]$TaskConfigPath
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        if (-not($PSBoundParameters.ContainsKey('TaskConfigPath'))) {
            Write-Debug "No TaskConfigPath given. Looking for BuildConfigPath"
            $possibleBuildConfigPath = $PSCmdlet.GetVariableValue('BuildConfigPath')
            if (-not ([string]::IsNullorEmpty($possibleBuildConfigPath))) {
                Write-Debug "found BuildConfigPath at $possibleBuildConfigPath"
                $BuildConfigPath = $possibleBuildConfigPath
                $TaskConfigPath = (Join-Path -Path $BuildConfigPath -ChildPath 'config' -AdditionalChildPath 'tasks')
                Remove-Variable possibleBuildConfigPath -ErrorAction SilentlyContinue
            }
        }
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        if ($null -eq $TaskConfigPath) {
            throw "Could not find $($Task.Name) configuration because TaskConfigPath was not set"
        }

        if (Test-Path $TaskConfigPath) {
        Write-Debug "Looking for task config files in $TaskConfigPath"
            $taskConfigFiles = Get-ChildItem -Path $TaskConfigPath -Filter "*.config.psd1"
            Write-Debug " - Found $($taskConfigFiles.Count) config files"
            foreach ($taskConfigFile in $taskConfigFiles) {
                if ((-not ($PSBoundParameters.ContainsKey('Task'))) -or
                ($TaskConfigFile.BaseName -like "$($Task.Name).config"))  {
                    try {
                        $config = Import-Psd -Path $taskConfigFile -Unsafe
                        $config['TaskName'] = ($TaskConfigFile.BaseName -replace '\.config$', '')
                    } catch {
                        throw "THere was an error loading $taskConfigFile `n$_"
                    }
                    $config | Write-Output
                }
            }

        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Configuration\Get-TaskConfiguration.ps1 60
#region source\stitch\public\Configuration\Merge-BuildConfiguration.ps1 1
function Merge-BuildConfiguration {
    [CmdletBinding()]
    param(
        # Specifies a path to one or more configuration files
        [Parameter(
        Position = 2,
        ValueFromPipeline,
        ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string[]]$Path,

        # The object to merge the configuration into (by reference)
        [Parameter(
            Mandatory,
            Position = 0
        )]
        [ref]$Object,

        # The top level key in which to add the given table
        [Parameter(
            Position = 1
        )]
        [string]$Key
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        foreach ($file in $Path) {
            $options = Convert-ConfigurationFile $Path
            if ($null -ne $options) {
                $Object.Value | Update-Object -UpdateObject $options
            }
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Configuration\Merge-BuildConfiguration.ps1 42
#region source\stitch\public\Configuration\Select-BuildRunBook.ps1 2
function Select-BuildRunBook {
    <#
    .SYNOPSIS
        Locate the runbook for the given BuildProfile
    .DESCRIPTION
        Select-BuildRunBook locates the runbook associated with the BuildProfile. If no BuildProfile is given,
        Select-BuildRunBook will use default names to search for
    .EXAMPLE
        $ProfilePath | Select-BuildRunBook 'default'
        $ProfilePath | Select-BuildRunBook 'site'
    #>

    [CmdletBinding()]
    param(
        # Specifies a path to one or more locations.
        [Parameter(
            Position = 1,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
            )]
            [Alias('PSPath')]
            [string[]]$Path,

        # The build profile to select the runbook for
        [Parameter(
            Position = 0
        )]
        [string]$BuildProfile
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $defaultProfileNames = @(
            'default',
            'build'
        )
        $defaultRunbookSuffix = "runbook.ps1"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        if (-not ($PSBoundParameters.ContainsKey('Path'))) {
            if (-not ([string]::IsNullorEmpty($PSCmdlet.GetVariableValue('ProfileRoot')))) {
                $Path = $PSCmdlet.GetVariableValue('ProfileRoot')
            } else {
                $Path = (Get-Location)
            }
        }

        if (-not ($PSBoundParameters.ContainsKey('BuildProfile'))) {
            $searches = $defaultProfileNames
        } else {
            $searches = $BuildProfile
        }

        foreach ($p in $Path) {
            if (Test-Path $p) {
                foreach ($searchFor in $searches) {
                    Write-Debug "Looking in $p for $searchFor runbook"
                    <#
                    First, look for the buildprofile.runbook.ps1 in the given directory
                    #>

                    $options = @{
                        Path = $p
                        Filter = "$searchFor.$defaultRunbookSuffix"
                    }
                    $possibleRunbook = Get-ChildItem @options | Select-Object -First 1

                    if ($null -eq $possibleRunbook) {
                        Write-Debug " - No runbook found in $p matching $($options.Filter)"
                        $null = $options.Clear()
                        $options = @{
                            Path = (Join-Path $p $searchFor)
                            Filter = "*$defaultRunbookSuffix"
                        }
                        Write-Debug "Looking in $($options.Path) using $($options.Filter)"
                        if (Test-Path $options.Path) {
                            $possibleRunbook = Get-ChildItem @options | Select-Object -First 1
                        }
                    }

                    if ($null -ne $possibleRunbook) {
                        $possibleRunbook | Write-Output
                    }
                }
            }
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }

}
#endregion source\stitch\public\Configuration\Select-BuildRunBook.ps1 92
#region source\stitch\public\Content\Convert-LineEnding.ps1 2
function Convert-LineEnding {
    <#
    .SYNOPSIS
        Convert the line endings in the given file to "Windows" (CRLF) or "Unix" (LF)
    .DESCRIPTION
        `Convert-LineEnding` will convert all of the line endings in the given file to the type specified. If
        'Windows' or 'CRLF' is given, all line endings will be '\r\n' and if 'Unix' or 'LF' is given all line
        endings will be '\n'
 
        'Unix' (LF) is the default
    .EXAMPLE
        Get-ChildItem . -Filter "*.txt" | Convert-LineEnding -LF
 
        Convert all txt files in the current directory to '\n'
    .NOTES
        WARNING! this can corrupt a binary file.
    #>

    [CmdletBinding(
        DefaultParameterSetName = 'Unix'
    )]
    param(
        # The file to be converted
        [Parameter(
            Position = 0,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string[]]$Path,

        # Convert line endings to 'Unix' (LF)
        [Parameter(
            ParameterSetName = 'Unix',
            Position = 1
        )]
        [switch]$LF,

        # Convert line endings to 'Windows' (CRLF)
        [Parameter(
            ParameterSetName = 'Windows',
            Position = 1
        )]
        [switch]$CRLF
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        foreach ($file in $Path) {
            if ($CRLF) {
                Write-Verbose " Converting line endings in $($file.Name) to 'CRLF'"
            ((Get-Content $file) -join "`r`n") | Set-Content -NoNewline -Path $file
            } elseif ($LF) {
                Write-Verbose " Converting line endings in $($file.Name) to 'LF'"
            ((Get-Content $file) -join "`n") | Set-Content -NoNewline -Path $file
            } else {
                Write-Error "No EOL format specified. Please use '-LF' or '-CRLF'"
            }
        }
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Content\Convert-LineEnding.ps1 65
#region source\stitch\public\Content\Find-ParseToken.ps1 2
function Find-ParseToken {
    <#
    .SYNOPSIS
        Return an array of tokens that match the given pattern
    #>

    [OutputType([System.Array])]
    [CmdletBinding()]
    param(
        # The token to find, as a regex
        [Parameter(
            Position = 0
        )]
        [string]$Pattern,

        # The type of token to look in
        [Parameter(
            Position = 1
        )]
        [System.Management.Automation.PSTokenType]$Type,

        # Specifies a path to one or more locations.
        [Parameter(
        Position = 2,
        ValueFromPipeline,
        ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string]$Path
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $options = $PSBoundParameters
        $null = $options.Remove('Pattern')
        try {
            $tokens = Get-ParseToken @options
        }
        catch {
            throw "Could not parse $Path`n$_"
        }

        if ($null -ne $tokens) {
            Write-Debug " - Looking for $Pattern in $($tokens.Count) tokens"
            foreach ($token in $tokens) {
                Write-Debug " - Checking $($token.Content)"
                if ($token.Content -Match $Pattern) {
                    $token | Write-Output

                }
            }
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Content\Find-ParseToken.ps1 60
#region source\stitch\public\Content\Format-File.ps1 2
function Format-File {
    <#
    .SYNOPSIS
        Run PSSA formatter on the given files
    #>

    [CmdletBinding()]
    param(
        # Specifies a path to one or more locations.
        [Parameter(
            Position = 1,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string[]]$Path,

        # Path to the code format settings
        [Parameter(
            Position = 0
        )]
        [object]$Settings = 'CodeFormatting.psd1'
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"

        if (-not($PSBoundParameters.ContainsKey('Path'))) {
            if ($null -ne $psEditor) {
                $currentFile = $psEditor.GetEditorContext().CurrentFile.Path
                if (Test-Path $currentFile) {
                    Write-Debug "Formatting current VSCode file '$currentFile'"
                    $Path += $currentFile
                }
            }
        }
        foreach ($file in $Path) {
            if (Test-Path $file) {
                $content = Get-Content $file -Raw
                $options = @{
                    ScriptDefinition = $content
                    Settings         = $Settings
                }
                try {
                    Invoke-Formatter @options | Set-Content $file
                } catch {
                    $PSCmdlet.ThrowTerminatingError($_)
                }
            }
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Content\Format-File.ps1 58
#region source\stitch\public\Content\Get-ParseToken.ps1 2
function Get-ParseToken {
    <#
    .SYNOPSIS
        Return an array of Tokens from parsing a file
    #>

    [CmdletBinding()]
    param(
        # Specifies a path to one or more locations.
        [Parameter(
        Position = 0,
        ValueFromPipeline,
        ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string[]]$Path,

        # The type of token to return
        [Parameter(
        )]
        [System.Management.Automation.PSTokenType]$Type
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        if (Test-Path $Path) {
            $errors = @()
            $content = Get-Item $Path | Get-Content -Raw
            $parsedText = [System.Management.Automation.PSParser]::Tokenize($content, [ref]$errors)

            if ($errors.Count) {
                throw "There were errors parsing $($Path.FullName). $($errors -join "`n")"
            }
            foreach ($token in $parsedText) {
                if ((-not($PSBoundParameters.ContainsKey('Type'))) -or
                    ($token.Type -like $Type)) {
                        $token | Write-Output
                }
            }

        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }

}
#endregion source\stitch\public\Content\Get-ParseToken.ps1 50
#region source\stitch\public\Content\Invoke-ReplaceToken.ps1 2
function Invoke-ReplaceToken {
    <#
    .SYNOPSIS
        Replace a given string 'Token' with another string in a given file.
    #>

    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact = 'medium'
    )]
    param(

        # File(s) to replace tokens in
        [Parameter(
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath', 'Path')]
        [string]$In,

        # The token to replace, written as a regular-expression
        [Parameter(
            Position = 0,
            Mandatory
        )]
        [string]$Token,

        # The value to replace the token with
        [Parameter(
            Position = 1,
            Mandatory
        )]
        [Alias('Value')]
        [string]$With,


        # The destination file to write the new content to
        # If destination is a directory, `Invoke-ReplaceToken` will put the content in a file named the same as
        # the input, but in the given directory
        [Parameter(
            Position = 2
            )]
            [Alias('Out')]
            [string]$Destination
    )
    begin {
    }
    process {
        try {
            $content = Get-Content $In -Raw
            if ($content | Select-String -Pattern $Token) {
                Write-Debug "Token $Token found, replacing with $With"
                $newContent = ($content -replace [regex]::Escape($Token), $With)

                if ($PSBoundParameters.ContainsKey('Destination')) {
                    $destObject = Get-Item $Destination
                    if ($destObject -is [System.IO.FileInfo]) {
                        $destFile = $Destination
                    } elseif ($destObject -is [System.IO.DirectoryInfo]) {
                        $destFile = (Join-Path $Destination ((Get-Item $file).Name))
                    } else {
                        throw "$Destination should be a file or directory"
                    }
                } else {
                    $newContent | Write-Output
                }
                if ($PSCmdlet.ShouldProcess($destFile, "Replace $Token with $With")) {
                    Write-Verbose "Writing output to $destFile"
                    $newContent | Set-Content $destFile -Encoding utf8NoBOM
                }
            } else {
                #! This is a little rude, but I have to find a way to let the user know that nothing changed,
                #! and I don't want to send anything out to the console in case it is being directed somewhere
                #TODO: Consider using Write-Warning
                $save_verbose = $VerbosePreference
                $VerbosePreference = 'Continue'
                Write-Verbose "$Token not found in $In"
                $VerbosePreference = $save_verbose
            }
        } catch {
            $PSCmdlet.ThrowTerminatingError($_)
        }
    }
    end {
    }
}
#endregion source\stitch\public\Content\Invoke-ReplaceToken.ps1 87
#region source\stitch\public\Content\Measure-File.ps1 2
function Measure-File {
    <#
    .SYNOPSIS
        Run PSSA analyzer on the given files
    #>

    [CmdletBinding()]
    param(
        # Specifies a path to one or more locations.
        [Parameter(
            Position = 0,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string[]]$Path,

        # Path to the code format settings
        [Parameter(
        )]
        [object]$Settings = 'PSScriptAnalyzerSettings.psd1',

        # Optionally apply fixes
        [Parameter(
        )]
        [switch]$Fix
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        if (-not($PSBoundParameters.ContainsKey('Path'))) {
            if ($null -ne $psEditor) {
                $currentFile = $psEditor.GetEditorContext().CurrentFile.Path
                if (Test-Path $currentFile) {
                    Write-Debug "Formatting current VSCode file"
                    $Path += $currentFile
                }
            }
        }
        foreach ($file in $Path) {
            if (Test-Path $file) {
                $options = @{
                    Path     = $file
                    Settings = $Settings
                    Fix      = $Fix
                }
                try {
                    Invoke-ScriptAnalyzer @options
                } catch {
                    $PSCmdlet.ThrowTerminatingError($_)
                }
            }
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Content\Measure-File.ps1 61
#region source\stitch\public\Content\Merge-SourceItem.ps1 2
function Merge-SourceItem {
    [CmdletBinding()]
    param(
        # The SourceItems to be merged
        [Parameter(
            ValueFromPipeline
        )]
        [PSTypeName('Stitch.SourceItemInfo')][object[]]$SourceItem,

        # File to merge the SourceItem into
        [Parameter(
            Position = 0
        )]
        [string]$Path,

        # Optionally wrap the given source items in `#section/endsection` tags
        [Parameter(
        )]
        [string]$AsSection
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $pre = '#region {0} {1}'
        $post = '#endregion {0} {1}'
        $root = Resolve-ProjectRoot

        $sb = New-Object System.Text.StringBuilder

        if ($PSBoundParameters.ContainsKey('AsSection')) {
            $null = $sb.AppendJoin('', @('#', ('=' * 79))).AppendLine()
            $null = $sb.AppendFormat( '#region {0}', $AsSection).AppendLine()
        }

        #-------------------------------------------------------------------------------
        #region Setup
        $sourceInfoUsingStatements = [System.Collections.ArrayList]@()

        $sourceInfoRequires = [System.Collections.ArrayList]@()
        #endregion Setup
        #-------------------------------------------------------------------------------


    }
    process {
        Write-Debug "Processing SourceItem $($PSItem.Name)"

        #-------------------------------------------------------------------------------
        #region Parse SourceItem
        #-------------------------------------------------------------------------------
        #region Content
        Write-Debug "Parsing SourceItem $($PSItem.Name)"
        #! The first NamedBlock in the AST *should* be the enum, class or function
        $predicate = { param($a) $a -is [NamedBlockAst] }
        $ast = $PSItem.Ast
        if ($null -eq $ast) { throw "Could not parse $($PSItem.Name)" }
        $nb = $ast.Find($predicate, $false)

        $start = $nb.Extent.StartLineNumber
        $end = $nb.Extent.EndLineNumber
        Write-Debug " - First NamedBlock found starting on line $start ending on line $end"

        $relativePath = $PSItem.Path -replace [regex]::Escape($root) , ''
        #! remove the leading '\' if it's there
        if ($relativePath.SubString(0,1) -like '\') {
            $relativePath = $relativePath.Substring(1,($relativePath.Length - 1))
        }

        Write-Debug " - Setting relative path to $relativePath"
        #endregion Content
        #-------------------------------------------------------------------------------

        #-------------------------------------------------------------------------------
        #region Using statements
        if ($ast.UsingStatements.Count -gt 0) {
            Write-Debug ' - Storing using statements'
            $sourceInfoUsingStatements += $ast.UsingStatements
        }

        #endregion Using statements
        #-------------------------------------------------------------------------------

        #-------------------------------------------------------------------------------
        #region Requires statements

        if ($ast.ScriptRequirements.Count -gt 0) {
            Write-Debug ' - Storing Requires statements'
            $sourceInfoRequires += $ast.ScriptRequirements
        }
        #endregion Requires statements
        #-------------------------------------------------------------------------------

        #endregion Parse SourceItem
        #-------------------------------------------------------------------------------

        #-------------------------------------------------------------------------------
        #region SourceItem content
        Write-Debug " - Merging $($PSItem.Name) contents"
        $null = $sb.AppendFormat( $pre, $relativePath, $start ).AppendLine()
        $null = $sb.Append( $nb.Extent.Text).AppendLine()
        $null = $sb.AppendFormat( $post, $relativePath, $end).AppendLine()

        #endregion SourceItem content
        #-------------------------------------------------------------------------------
    }
    end {
        #-------------------------------------------------------------------------------
        #region Update module content

        #-------------------------------------------------------------------------------
        #region Add sourceItem
        if ($PSBoundParameters.ContainsKey('AsSection')) {
            $null = $sb.AppendFormat( '#endregion {0}', $AsSection).AppendLine()
            $null = $sb.AppendJoin('', @('#', ('=' * 79))).AppendLine()
        }

        Write-Debug "Writing new content to $Path"
        $sb.ToString() | Add-Content $Path
        $null = $sb.Clear()
        #endregion Add sourceItem
        #-------------------------------------------------------------------------------
        #-------------------------------------------------------------------------------
        #region Parse module
        Write-Debug "$Path exists. Parsing contents"
        $moduleText = Get-Content $Path
        $module = [Parser]::ParseInput($moduleText, [ref]$null, [ref]$null)
        $content = $moduleText
        #endregion Parse module
        #-------------------------------------------------------------------------------

        #-------------------------------------------------------------------------------
        #region Requires statements
        Write-Debug ' - Parsing Requires statements'
        $combinedRequires = $module.ScriptRequirements + $sourceInfoRequires

        if (-not([string]::IsNullorEmpty($combinedRequires.ScriptRequirements.RequiredApplicationId))) {
            $s = "#Requires -ShellId $($combinedRequires.ScriptRequirements.RequiredApplicationId)"
            $content = ($content) -replace [regex]::Escape($s), ''
            $null = $sb.AppendLine($s)
            Remove-Variable s
        }
        if (-not([string]::IsNullorEmpty($combinedRequires.ScriptRequirements.RequiredPSVersion))) {
            $s = "#Requires -Version $($combinedRequires.ScriptRequirements.RequiredPSVersion)"
            $content = ($content) -replace [regex]::Escape($s), ''
            $null = $sb.AppendLine($s)
            Remove-Variable s
        }
        foreach ($rm in $combinedRequires.ScriptRequirements.RequiredModules) {
            $s = "#Requires -Modules $($rm.ToString())"
            $content = ($content) -replace [regex]::Escape($s), ''
            $null = $sb.AppendLine($s)
            Remove-Variable s
        }
        foreach ($ra in $combinedRequires.ScriptRequirements.RequiredAssemblies) {
            $s = "#Requires -Assembly $ra"
            $content = ($content) -replace [regex]::Escape($s), ''
            $null = $sb.AppendLine($s)
            Remove-Variable s
        }
        foreach ($re in $combinedRequires.ScriptRequirements.RequiredPSEditions) {
            $s = "#Requires -PSEdition $re"
            $content = ($content) -replace [regex]::Escape($s), ''
            $null = $sb.AppendLine($s)
            Remove-Variable s
        }
        foreach ($rp in $combinedRequires.ScriptRequirements.RequiresPSSnapIns) {
            $s = "#Requires -PSnapIn $($rp.Name)"
            if (-not([string]::IsNullorEmpty($rp.Version))) {
                $s += " -Version $(rp.Version)"
            }
            $content = ($content) -replace [regex]::Escape($s), ''
            $null = $sb.AppendLine($s)
            Remove-Variable s
        }

        if ($combinedRequires.ScriptRequirements.IsElevationRequired) {
            $s = '#Requires -RunAsAdministrator'
            $content = ($content) -replace [regex]::Escape($s), ''
            $null = $sb.AppendLine($s)
            Remove-Variable s
        }
        #endregion Requires statements
        #-------------------------------------------------------------------------------

        #-------------------------------------------------------------------------------
        #region Using statements
        $combinedUsingStatements = $module.UsingStatements + $sourceInfoUsingStatements

        if ($combinedUsingStatements.Count -gt 0) {
            Write-Debug " - Parsing using statements in $Path"
            Write-Debug "There are $($combinedUsingStatements.Count) using statements"
            Write-Debug "$($combinedUsingStatements | Select-Object Name, UsingStatementKind | Out-String)"
            foreach ($kind in [UsingStatementKind].GetEnumValues()) {
                Write-Debug " - Checking for using $kind statements"
                $statements = $combinedUsingStatements | Where-Object UsingStatementKind -Like $kind

                if ($statements.Count -gt 0) {
                    Write-Debug " - $($statements.Count) found"
                    $added = @()
                    foreach ($statement in $statements) {
                        $s = $statement.Extent.Text
                        if ($added -contains $s) {
                            Write-Debug " - '$s' already processed"
                        } else {
                            Write-Debug " - Looking for '$s' in content"
                            # first, remove the line from the original content
                            if (($content) -match [regex]::Escape($s)) {
                                Write-Debug " - found '$s' in content"
                                $content = ($content) -replace [regex]::Escape($s), ''
                            }
                            $null = $sb.AppendLine($s)
                            $added += $s
                        }
                    }
                }
            }
        } else {
            Write-Debug 'No using statements in module or sourceInfo'
        }
        #endregion Using statements
        #-------------------------------------------------------------------------------

        #-------------------------------------------------------------------------------
        #region Content
        Write-Debug "Writing content back to $Path"
        $null = $sb.AppendJoin("`n", $content)
        $sb.ToString() | Set-Content $Path
        #endregion Content
        #-------------------------------------------------------------------------------

        #endregion Update module content
        #-------------------------------------------------------------------------------

    }
}
#endregion source\stitch\public\Content\Merge-SourceItem.ps1 235
#region source\stitch\public\Content\Test-WindowsLineEnding.ps1 2
function Test-WindowsLineEnding {
    <#
    .SYNOPSIS
        Test for "Windows Line Endings" (CRLF) in the given file
    .DESCRIPTION
        `Test-WindowsLineEnding` returns true if the file contains CRLF endings, and false if not
    #>

    [CmdletBinding()]
    param(
        # Specifies a path to one or more locations.
        [Parameter(
            Position = 0,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [ValidateNotNullOrEmpty()]
        [string]$Path
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        if (Test-Path $Path) {
            (Get-Content $Path -Raw) -match '\r\n$'
        } else {
            Write-Error "$Path could not be found"
        }
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Content\Test-WindowsLineEnding.ps1 34
#region source\stitch\public\FeatureFlags\Get-FeatureFlag.ps1 2
function Get-FeatureFlag {
    <#
    .SYNOPSIS
        Retrieve feature flags for the stitch module
    #>

    [CmdletBinding()]
    param(
        # The name of the feature flag to test
        [Parameter(
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [string]$Name
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $featureFlagFile = (Join-Path (Get-ModulePath) 'feature.flags.config.psd1')
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        if ($null -ne $BuildInfo.Flags) {
            Write-Debug "Found the buildinfo table and it has Flags set"
            $featureFlags = $BuildInfo.Flags
        } elseif ($null -ne $featureFlagFile) {
            if (Test-Path $featureFlagFile) {
                $featureFlags = Import-Psd $featureFlagFile -Unsafe
            }
        }

        if ($null -ne $featureFlags) {
            switch ($featureFlags) {
                ($_ -is [System.Collections.Hashtable]) {
                    foreach ($key in $featureFlags.Keys) {
                        $flag =  $featureFlags[$key]
                        $flag['PSTypeName'] = 'Stitch.FeatureFlag'
                        $flag['Name'] = $key

                        if ((-not ($PSBoundParameters.ContainsKey('Name'))) -or
                        ($flag.Name -like $Name)) {
                            [PSCustomObject]$flag | Write-Output
                        }
                        continue
                    }
                }
                default {
                    foreach ($flag in $featureFlags.PSobject.properties) {
                        Write-Debug "Name is $($flag.Name)"
                        if ((-not ($PSBoundParameters.ContainsKey('Name'))) -or
                        ($flag.Name -like $Name)) {
                            $flag | Write-Output
                        }
                    }
                }
            }
        } else {
            Write-Information "No feature flag data was found"
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\FeatureFlags\Get-FeatureFlag.ps1 64
#region source\stitch\public\FeatureFlags\Test-FeatureFlag.ps1 1
function Test-FeatureFlag {
    <#
    .SYNOPSIS
        Test if a feature flag is enabled
    #>

    [OutputType([bool])]
    [CmdletBinding()]
    param(
        # The name of the feature flag to test
        [Parameter(
            Mandatory
        )]
        [ValidateNotNullOrEmpty()]
        [string]$Name
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $flag = Get-FeatureFlag -Name $Name

        if ([string]::IsNullorEmpty($flag)) {
            $false
        } else {
            $flag.Enabled
        }

        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\FeatureFlags\Test-FeatureFlag.ps1 34
#region source\stitch\public\Git\Add-GitFile.ps1 2
function Add-GitFile {
    <#
    .EXAMPLE
        Get-ChildItem *.md | function Add-GitFile
    .EXAMPLE
        Get-GitStatus | function Add-GitFile
    #>

    [CmdletBinding(
        DefaultParameterSetName = 'asPath'
    )]
    param(
        # Accept a statusentry
        [Parameter(
            ParameterSetName = 'asEntry',
            ValueFromPipeline
        )]
        [LibGit2Sharp.RepositoryStatus[]]$Entry,

        # Paths to files to add
        [Parameter(
            Position = 0,
            ParameterSetName = 'asPath',
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string[]]$Path,


        # Add All items
        [Parameter(
        )]
        [switch]$All,

        # The repository root
        [Parameter(
        )]
        [string]$RepoRoot,

        # Return objects to the pipeline
        [Parameter(
        )]
        [switch]$PassThru
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        if ($PSBoundParameters.ContainsKey('Entry')) {
            $PSBoundParameters['Path'] = @()
            Write-Debug ' processing entry'
            foreach ($e in $Entry) {
                Write-Debug " - adding $($e.FilePath)"
                $PSBoundParameters['Path'] += $e.FilePath
            }
        }
        foreach ($file in $Path) {
            Add-GitItem (Resolve-Path $file -Relative)
        }
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Git\Add-GitFile.ps1 65
#region source\stitch\public\Git\Checkpoint-GitWorkingDirectory.ps1 2
function Checkpoint-GitWorkingDirectory {
    <#
    .SYNOPSIS
        Save all changes (including untracked) and push to upstream
    #>

    [CmdletBinding()]
    param(
        # Message to use for the checkpoint commit.
        # Defaults to:
        # `[checkpoint] Creating checkpoint before continuing <date>`
        [Parameter(
        )]
        [string]$Message
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        if (-not ($PSBoundParameters.ContainsKey('Message'))) {
            $Message = "[checkpoint] Creating checkpoint before continuing $(Get-Date -Format FileDateTimeUniversal)"
        }
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"

        Write-Verbose 'Staging all changes'
        Add-GitItem -All
        Write-Verbose 'Commiting changes'
        Save-GitCommit -Message $Message
        Write-Verbose 'Pushing changes upstream'
        if (-not(Get-GitBranch -Current | Select-Object -ExpandProperty IsTracking)) {
            Get-GitBranch -Current | Send-GitBranch -SetUpstream
        } else {
            Get-GitBranch -Current | Send-GitBranch
        }

        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Git\Checkpoint-GitWorkingDirectory.ps1 41
#region source\stitch\public\Git\Clear-MergedGitBranch.ps1 2
function Clear-MergedGitBranch {
    <#
    .SYNOPSIS
        Prune remote branches and local branches with no tracking branch
    #>

    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact = 'High'
    )]
    param(
        # Only clear remote branches
        [Parameter(
            ParameterSetName = 'Remote'
        )]
        [switch]$RemoteOnly,
        # Only clear remote branches
        [Parameter(
            ParameterSetName = 'Local'
        )]
        [switch]$LocalOnly
    )
    Write-Verbose "Pruning remote first"
    if (-not ($LocalOnly)) {
        if ($PSCmdlet.ShouldProcess("Remote origin", "Prune")) {
            #TODO: Find a "PowerGit way" to do this part
            git remote prune origin
        }
    }
    if (-not ($RemoteOnly)) {
        $branches = Get-GitBranch | Where-Object { $_.IsTracking -and $_.TrackedBranch.IsGone }
        if ($null -ne $branches) {
            Write-Verbose "Removing $($branches.Count) local branches"
            foreach ($branch in $branches) {
                if ($PSCmdlet.ShouldProcess($branch.FriendlyName, "Remove branch")) {
                    Remove-GitBranch
                }
            }
        }
    }
}
#endregion source\stitch\public\Git\Clear-MergedGitBranch.ps1 41
#region source\stitch\public\Git\ConvertFrom-ConventionalCommit.ps1 2
function ConvertFrom-ConventionalCommit {
    <#
    .SYNOPSIS
        Convert a git commit message (such as from PowerGit\Get-GitCommit) into an object on the pipeline
    .DESCRIPTION
        A git commit message is technically unstructured text. However, a long standing convention is to structure
        the message should be a single line title, followed by a blank line and then any amount of text in the body.
        Conventional Commits provide additional structure by adding "metadata" to the title:
 
        -
        | |<------ title ----------------------| <- 50 char or less
        | <type>[optional scope]: <description>
        message
        | [optional body] <- 72 char or less
        |
        | [optional footer(s)] <- 72 char or less
        -
        Recommended types are:
        - build
        - chore
        - ci
        - docs
        - feat
        - fix
        - perf
        - refactor
        - revert
        - style
        - test
 
    #>

    [CmdletBinding()]
    param(
        # The commit message to parse
        [Parameter(
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [string]$Message,

        [Parameter(
            ValueFromPipelineByPropertyName
        )]
        [object]$Sha,

        [Parameter(
            ValueFromPipelineByPropertyName
        )]
        [object]$Author,

        [Parameter(
            ValueFromPipelineByPropertyName
        )]
        [object]$Committer

    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        enum Section {
            NONE = 0
            HEAD = 1
            BODY = 2
            FOOT = 3
        }
    }
    process {
        # This will restart for each message on the pipeline
        # Messages (at least the ones from PowerGit objects) are multiline strings
        $section = [Section]::NONE
        $title = $type = $scope = ''
        $body = [System.Collections.ArrayList]@()
        $footers = @{}
        $breakingChange = $false
        $conforming = $false
        $lineNum = 1
        foreach ($line in ($Message -split '\n')) {
            try {
                Write-Debug "Parsing line #$lineNum : '$line'"
                switch -Regex ($line) {
                    '^#+' {
                        Write-Debug ' - Comment line'
                        continue
                    }
                    #! This may match the head, but also may match a specific kind of footer
                    #! too. So we check the line number and go from there
                    @'
(?x) # Matches either a conventional title <type>(<scope>)!: <description>
                  # or a footer of like <type>: <description>
^(?<t>\w+) # Header must start with a type word
(\((?<s>\w+)\))? # Optionally a scope in '()'
(?<b>!)? # Optionally a ! to denote a breaking change
:\s+ # Mandatory colon and a space
(?<d>.+)$ # Everything else is the description
'@
              {
                        Write-Debug ' - Head line'
                        # Parse as a heading only if we are on line one!
                        if ($lineNum -eq 1) {
                            $title = $line
                            $type = $Matches.t
                            $scope = $Matches.s ?? ''
                            $desc = $Matches.d
                            $section = [Section]::HEAD
                            $breakingChange = ($Matches.b -eq '!')
                            $conforming = $true
                        } else {
                            Write-Debug ' - Footer'
                            # There could be multiple entries of the same type of footer
                            # such as:
                            # closes #9
                            # closes #7
                            if ($footers.ContainsKey($Matches.t)) {
                                $footers[$Matches.t] += $Matches.d
                            } else {
                                $footers[$Matches.t] = @($Matches.d)
                            }
                            $section = [Section]::FOOT
                        }
                        continue
                    }
                    @'
(?x) # Matches a git-trailer style footer <type>: <description> or <type> #<description>
^\s*
(?<t>[a-zA-Z0-9-]+)
(:\s|\s\#)
(?<v>.*)$
'@
              {
                        Write-Debug ' - Footer'
                        # There could be multiple entries of the same type of footer
                        # such as:
                        # closes #9
                        # closes #7
                        if ($footers.ContainsKey($Matches.t)) {
                            $footers[$Matches.t] += $Matches.d
                        } else {
                            $footers[$Matches.t] = @($Matches.d)
                        }
                        $section = [Section]::FOOT
                        continue
                    }
                    @'
(?x) # Matches either BREAKING CHANGE: <description> or BREAKING-CHANGE: <description>
^\s*
(?<t>BREAKING[- ]CHANGE)
:\s
(?<v>.*)$
'@
              {
                        Write-Debug ' - Breaking change footer'
                        $footers[$Matches.t] = $Matches.v
                        $breakingChange = $true
                    }
                    '^\s*$' {
                        # might be the end of a section, or it might be in the middle of the body
                        if ($section -eq [Section]::HEAD) {
                            # this is our "one blank line convention"
                            # so the next line should be the start of the body
                            $section = [Section]::BODY
                        }
                        continue
                    }
                    Default {
                        #! if the first line is not in the proper format, it will
                        #! end up here: We can add it as the title, but none of
                        #! the conventional commit specs will be filled
                        if ($lineNum -eq 1) {
                            Write-Verbose " '$line' does not seem to be a conventional commit"
                            $title = $line
                            $desc = $line
                            $conforming = $false
                        } else {
                            # if it matched nothing else, it should be in the body
                            Write-Debug ' - Default match, adding to the body text'
                            $body += $line
                        }
                        continue
                    }
                }
            } catch {
                throw "At $lineNum : '$line'`n$_"
            }
            $lineNum++
        }

        [PSCustomObject]@{
            PSTypeName       = 'Git.ConventionalCommitInfo'
            Message          = $Message
            IsConventional   = $conforming
            IsBreakingChange = $breakingChange
            Title            = $title
            Type             = $type
            Scope            = $scope
            Description      = $desc
            Body             = $body
            Footers          = $footers
            Sha              = $Sha
            ShortSha         = $Sha.Substring(0, 7)
            Author           = $Author
            Committer        = $Committer

        } | Write-Output
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Git\ConvertFrom-ConventionalCommit.ps1 205
#region source\stitch\public\Git\Get-GitFile.ps1 1
function Get-GitFile {
    <#
    .SYNOPSIS
        Return a list of the files listed in git status
    #>

    [OutputType([System.IO.FileInfo])]
    [CmdletBinding()]
    param(
        # The type of files to return
        [Parameter(
        )]
        [ValidateSet(
            'Added', 'Ignored', 'Missing', 'Modified', 'Removed', 'Staged',
            'Unaltered', 'Untracked',
        'RenamedInIndex', 'RenamedInWorkDir', 'ChangedInIndex', 'ChangedInWorkDir')]
        [AllowNull()]
        [string]$Type
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"

    }
    process {
        if ($PSBoundParameters.ContainsKey('Type')) {
            $status = Get-GitStatus | Select-Object -ExpandProperty $Type
        } else {
            $status = Get-GitStatus
        }

        $status | Select-Object -ExpandProperty FilePath | ForEach-Object {
                Get-Item (Resolve-Path $_) | Write-Output
        }
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Git\Get-GitFile.ps1 37
#region source\stitch\public\Git\Get-GitHistory.ps1 2
function Get-GitHistory {
    [CmdletBinding()]
    param()

    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $config = Get-ChangelogConfig

        $currentVersion = $config.CurrentVersion ?? 'unreleased'

        $releases = @{
            $currentVersion = @{
                Name = $currentVersion
                Timestamp = (Get-Date)
                Groups = @{}
            }
        }
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"

        foreach ($commit in Get-GitCommit) {
            #-------------------------------------------------------------------------------
            #region Convert commit message
            Write-Debug "Converting $($commit.MessageShort)"
            try {
                $commitObject = $commit | ConvertFrom-ConventionalCommit
            }
            catch {
                $exception = [Exception]::new("Could not convert commit $($commit.MessageShort)`n$($_.PSMessageDetails)")
                $errorRecord = [System.Management.Automation.ErrorRecord]::new(
                    $exception,
                    $_.FullyQualifiedErrorId,
                    $_.CategoryInfo,
                $commit
                )
                $PSCmdlet.ThrowTerminatingError($errorRecord)
            }
            #endregion Convert commit message
            #-------------------------------------------------------------------------------



            if ($null -ne $commit.Refs) {
                foreach ($ref in $commit.Refs) {
                    $name = $ref.CanonicalName -replace '^refs\/', ''
                    if ($name -match '^tags\/(?<tag>.*)$') {
                        Write-Debug ' - is a tag'
                        $commitObject | Add-Member -NotePropertyName Tag -NotePropertyValue $Matches.tag
                        if ($commitObject.Tag -match $config.TagPattern) {
                            # Add a version to the releases
                            $currentVersion = $Matches.1
                            $releases[$currentVersion] = @{
                                Name = $currentVersion
                                Timestamp = (Get-Date '1970-01-01') # set it as the epoch, but update below
                                Groups    = @{}
                            }
                            if ($null -ne $commitObject.Author.When.UtcDateTime) {
                                $releases[$currentVersion].Timestamp = $commitObject.Author.When.UtcDateTime
                            }
                        }
                    }
                }
            }

            #-------------------------------------------------------------------------------
            #region Add to group

            $group = $commitObject | Resolve-ChangelogGroup

            if ($null -eq $group) {
                Write-Debug "no group information found for $($commitObject.MessageShort)"
                $group = @{
                    Name = 'Other'
                    DisplayName = 'Other'
                    Sort = 99999
                }
            }
                if (-not($releases[$currentVersion].Groups.ContainsKey($group.Name))) {
                    $releases[$currentVersion].Groups[$group.Name] = @{
                        DisplayName = $group.DisplayName
                        Sort = $group.Sort
                        Entries = @()
                    }
                }
                $releases[$currentVersion].Groups[$group.Name].Entries += $commitObject

            #endregion Add to group
            #-------------------------------------------------------------------------------
        }

        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {

        $releases | Write-Output
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }

}
#endregion source\stitch\public\Git\Get-GitHistory.ps1 101
#region source\stitch\public\Git\Get-GitHubDefaultBranch.ps1 1
function Get-GitHubDefaultBranch {
    <#
    .SYNOPSIS
        Returns the default branch of the given github repository
    #>

    [CmdletBinding()]
    param(
        # The repository to find the default brach in
        [Parameter(
        )]
        [string]$RepositoryName
    )

    if ($PSBoundParameters.Key -notcontains 'RepositoryName') {
        $RepositoryName = Get-GitRepository | Select-Object -ExpandProperty RepositoryName
    }

    Get-GitHubRepository -RepositoryName $RepositoryName | Select-Object -ExpandProperty DefaultBranch
}
#endregion source\stitch\public\Git\Get-GitHubDefaultBranch.ps1 19
#region source\stitch\public\Git\Get-GitMergedBranch.ps1 1
function Get-GitMergedBranch {
    <#
    .SYNOPSIS
        Return a list of branches that have been merged into the given branch (or default branch if none specified)
    #>

    [CmdletBinding()]
    param(
        # The branch to use for the "base" (the branch the returned branches are merged into)
        [Parameter(
            ValueFromPipelineByPropertyName
        )]
        [string]$FriendlyName = (Get-GitHubDefaultBranch)
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"

        $defaultTip = Get-GitBranch -Name $FriendlyName |
            Foreach-Object {$_.Tip.Sha }

        Get-GitBranch | Where-Object {
            ($_.FriendlyName -ne $FriendlyName) -and ($_.Commits |
                    Select-Object -ExpandProperty Sha) -contains $defaultTip
            } | Write-Output

        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Git\Get-GitMergedBranch.ps1 33
#region source\stitch\public\Git\Get-GitModifiedFile.ps1 1
function Get-GitModifiedFile {
    <#
    .SYNOPSIS
        Return a list of the files modified in the current repository
    #>

    [CmdletBinding()]
    param()
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Get-GitFile -Type Modified
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Git\Get-GitModifiedFile.ps1 17
#region source\stitch\public\Git\Get-GitRemoteTrackingBranch.ps1 1
function Get-GitRemoteTrackingBranch {
    Get-GitBranch | Select-Object -ExpandProperty TrackedBranch
}
#endregion source\stitch\public\Git\Get-GitRemoteTrackingBranch.ps1 3
#region source\stitch\public\Git\Get-GitStagedFile.ps1 2
function Get-GitStagedFile {
    <#
    .SYNOPSIS
        Return a list of the files modified in the current repository
    #>

    [CmdletBinding()]
    param()
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Get-GitFile -Type Staged
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Git\Get-GitStagedFile.ps1 18
#region source\stitch\public\Git\Get-GitUntrackedFile.ps1 2
function Get-GitUntrackedFile {
    <#
    .SYNOPSIS
        Return a list of the files untracked in the current repository
    #>

    [CmdletBinding()]
    param()
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Get-GitFile -Type Untracked
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Git\Get-GitUntrackedFile.ps1 18
#region source\stitch\public\Git\Join-PullRequest.ps1 1
function Join-PullRequest {
    <#
    .SYNOPSIS
        Merge the current branch's pull request, then pull them into '$DefaultBranch' (usually 'main' or 'master')
    .DESCRIPTION
        Ensuring the current branch is up-to-date on the remote, and that it has a pull-request,
        this function will then:
        1. Merge the current pull request
        1. Switch to the `$DefaultBranch` branch
        1. Pull the latest changes
    #>

    param(
        # The name of the repository. Uses the current repository if not specified
        [Parameter(
            ValueFromPipelineByPropertyName
        )]
        [string]$RepositoryName,

        # By default the remote and local branches are deleted if successfully merged. Add -DontDelete to
        # keep the branches
        [Parameter()]
        [switch]$DontDelete,


        # The default branch. usually 'main' or 'master'
        [Parameter(
        )]
        [string]$DefaultBranch

    )
    if (-not($PSBoundParameters.ContainsKey('RepositoryName'))) {
        $PSBoundParameters['RepositoryName'] = (Get-GitRepository | Select-ExpandProperty RepositoryName)
    }

    $status = Get-GitStatus
    if ($status.IsDirty) {
        throw "Changes exist in working directory.`nCommit or stash them first"
    } else {
        if (-not ($PSBoundParameters.ContainsKey('DefaultBranch'))) {
            $DefaultBranch = Get-GitHubDefaultBranch
        }

        if ([string]::IsNullorEmpty($DefaultBranch)) {
            throw "Could not determine default branch. Use -DefaultBranch parameter to specify"
        }

        $branch = Get-GitBranch -Current
        if ($null -ne $branch) {
            #-------------------------------------------------------------------------------
            #region Merge PullRequest
            Write-Debug "Getting Pull Request for branch $($branch.FriendlyName)"
            $pr = $branch | Get-GitHubPullRequest
            if ($null -ne $pr) {
                Write-Verbose "Merging Pull Request # $($pr.number)"
                try {
                    if ($DontDelete) {
                        $pr | Merge-GitHubPullRequest
                        Write-Verbose ' - (remote branch not deleted)'
                    } else {
                        $pr | Merge-GitHubPullRequest -DeleteBranch
                        Write-Verbose ' - (remote branch deleted)'
                    }
                } catch {
                    throw "Could not merge Pull Request`n$_"
                }

                #endregion Merge PullRequest
                #-------------------------------------------------------------------------------

                #-------------------------------------------------------------------------------
                #region Pull changes
                try {
                    Write-Verbose "Switching to branch '$DefaultBranch'"
                    Set-GitHead $DefaultBranch
                }
                catch {
                    throw "Could not switch to branch $DefaultBranch`n$_"
                }

                try {
                    Write-Verbose 'Pulling changes from remote'
                    Receive-GitBranch
                    Write-Verbose "Successfully merged pr #$($pr.number) and updated project"
                }
                catch {
                    throw "Could not update $DefaultBranch`n$_"
                }
                #endregion Pull changes
                #-------------------------------------------------------------------------------

                try {
                    Remove-GitBranch $branch
                }
                catch {
                    throw "Could not delete local branch $($branch.FriendlyName)"
                }
            } else {
                throw "Couldn't find a Pull Request for $($branch.FriendlyName)"
            }
        } else {
            throw "Couldn't get the current branch"
        }
    }
}
#endregion source\stitch\public\Git\Join-PullRequest.ps1 104
#region source\stitch\public\Git\Start-GitBranch.ps1 2
function Start-GitBranch {
    param(
        [string]$Name
    )
    New-GitBranch $Name | Set-GitHead
}
#endregion source\stitch\public\Git\Start-GitBranch.ps1 7
#region source\stitch\public\Git\Sync-GitRepository.ps1 2
function Sync-GitRepository {
    <#
    .SYNOPSIS
        Update the working directory of the current branch
    .DESCRIPTION
        This is equivelant to `git pull --rebase
    #>

    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact = 'Medium'
    )]
    param()
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"

        $br = Get-GitBranch -Current
        if ($br.IsTracking) {
            $remote = $br.TrackedBranch
            if ($PSCmdlet.ShouldProcess($br.FriendlyName, "Update")) {
                $br | Send-GitBranch origin
                Start-GitRebase -Upstream $remote.FriendlyName -Branch $br.FriendlyName
            }
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Git\Sync-GitRepository.ps1 33
#region source\stitch\public\Git\Undo-GitCommit.ps1 2
function Undo-GitCommit {
    <#
    .SYNOPSIS
        Reset the branch to before the previous commit
    .DESCRIPTION
        There are three types of reset:
        but keep all the changes in the working directory
        Without This is equivelant to `git reset HEAD~1 --mixed
    #>

    [CmdletBinding()]
    param(
        # Hard reset
        [Parameter(
            ParameterSetName = 'Hard'
        )]
        [switch]$Hard,

        # Soft reset
        [Parameter(
            ParameterSetName = 'Soft'
        )]
        [switch]$Soft



    )
    #! The default mode is mixed, it does not have a parameter
    Reset-GitHead -Revision 'HEAD~1' @PSBoundParameters
}
#endregion source\stitch\public\Git\Undo-GitCommit.ps1 30
#region source\stitch\public\Git\Update-GitRepository.ps1 2
function Update-GitRepository {
    <#
    .SYNOPSIS
        Update the working directory of the current branch
    .DESCRIPTION
        This is equivelant to `git pull --rebase
    #>

    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact = 'Medium'
    )]
    param()
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"

        $br = Get-GitBranch -Current
        if ($br.IsTracking) {
            $remote = $br.TrackedBranch
            if ($PSCmdlet.ShouldProcess($br.FriendlyName, "Update")) {
                Start-GitRebase -Upstream $remote.FriendlyName -Branch $br.FriendlyName
            }
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Git\Update-GitRepository.ps1 32
#region source\stitch\public\InvokeBuild\Get-BuildTask.ps1 2
function Get-BuildTask {
    [CmdletBinding()]
    param(
        # The name of the task to get
        [Parameter(
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [ArgumentCompleter({ Invoke-TaskNameCompletion @args })]
        [string]$Name
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        function Add-TaskProperty {
            param(
                [ref]$TaskRef
            )
            #! if the task was written as 'phase <name>' then the InvocationName
            #! can be used to find it. Add a property 'IsPhase' for easier sorting
            $TaskRef.Value | Add-Member -NotePropertyName Synopsis -NotePropertyValue (
                (Get-BuildSynopsis ${*}.All[$TaskRef.Value.Name] -ErrorAction SilentlyContinue) ?? ''
            )
            $TaskRef.Value | Add-Member -NotePropertyName IsPhase -NotePropertyValue (
                ( $TaskRef.Value.InvocationInfo.InvocationName -like 'phase' ) ? $true : $false
            )
            $TaskRef.Value | Add-Member -NotePropertyName Path -NotePropertyValue (
                Get-Item $TaskRef.Value.InvocationInfo.ScriptName
            )
            $TaskRef.Value | Add-Member -NotePropertyName Line -NotePropertyValue $TaskRef.Value.InvocationInfo.ScriptLineNumber
            $TaskRef.Value.PSObject.TypeNames.Insert(0, 'InvokeBuild.TaskInfo')
        }

    }
    process {
        if (Test-InInvokeBuild) {
            $taskData = ${*}.AllTasks
        } else {
            $taskData = Invoke-Build ??
        }
        if ($null -ne $taskData) {

            if ($PSBoundParameters.ContainsKey('Name')) {
                $task = $taskData[$Name]
                if (-not ([string]::IsNullorEmpty($task))) {
                    Add-TaskProperty ([ref]$task)
                    $task | Write-Output
                } else {
                    throw "There is no task named $Name in this project"
                }

            } else {
                foreach ($key in $taskData.Keys) {
                    $task = $taskData[$key]
                    Add-TaskProperty ([ref]$task)
                    $task | Write-Output
                }
            }
        }
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\InvokeBuild\Get-BuildTask.ps1 64
#region source\stitch\public\InvokeBuild\Get-TaskHelp.ps1 1
function Get-TaskHelp {
    <#
    .SYNOPSIS
        Retrieve the comment based help for the given task
    #>

    [CmdletBinding()]
    param(
        # The name of the task to get the help documentation for
        [Parameter(
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [ArgumentCompleter({ Invoke-TaskNameCompletion @args})]
        [string[]]$Name,

        # The InvocationInfo of a task
        [Parameter(
            ValueFromPipelineByPropertyName
        )]
        [System.Management.Automation.InvocationInfo]$InvocationInfo
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        if ($PSBoundParameters.ContainsKey('InvocationInfo')) {
            Get-Help $InvocationInfo.ScriptName -Full
        } else {
            $task = Get-BuildTask -Name:$Name
            if ($null -ne $task) {
                Get-Help $task.InvocationInfo.ScriptName -Full
            }
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\InvokeBuild\Get-TaskHelp.ps1 40
#region source\stitch\public\InvokeBuild\Test-InInvokeBuild.ps1 1
function Test-InInvokeBuild {
    [CmdletBinding()]
    param(
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $invokeBuildPattern = 'Invoke-Build.ps1'
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $callStack = Get-PSCallStack
        $inInvokeBuild = $false
        for ($i = 1; $i -lt $callStack.Length; $i++) {
            $caller = $callStack[$i]
            Write-Debug "This caller is $($caller.Command)"
            if ($caller.Command -match $invokeBuildPattern) {
                $inInvokeBuild = $true
                break
            }
        }
        $inInvokeBuild
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\InvokeBuild\Test-InInvokeBuild.ps1 27
#region source\stitch\public\Manifest\ConvertFrom-CommentedProperty.ps1 1
function ConvertFrom-CommentedProperty {
    <#
    .SYNOPSIS
        Uncomment the given Manifest Item
    .DESCRIPTION
        In a typical manifest, unused properties are listed, but commented out with a '#'
        like `# ReleaseNotes = ''`
        Update-Metadata, Import-Psd and similar functions need to have these fields available.
        `ConvertFrom-CommentedProperty` will remove the '#' from the line so that those functions can use the given
        property
    .EXAMPLE
        $manifest | ConvertFrom-CommentedProperty 'ReleaseNotes'
    #>

    [CmdletBinding()]
    param(
        # Specifies a path to one or more locations.
        [Parameter(
        ValueFromPipeline,
        ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string[]]$Path,

        # The item to uncomment
        [Parameter(
            Position = 0
        )]
        [Alias('PropertyName')]
        [string]$Property
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        if ($PSBoundParameters.ContainsKey('Path')) {
            if (Test-Path $Path) {
                $commentToken = $Path | Find-ParseToken -Type Comment -Pattern "^\s*#\s*$Property\s+=.*$" | Select-Object -First 1
                if ($null -ne $commentToken) {
                    $replacementIndent = (' ' * ($commentToken.StartColumn - 1))
                    $newContent = $commentToken.Content -replace '#\s*', $replacementIndent
                    $fileContent = @(Get-Content $Path)
                    $fileContent[$commentToken.StartLine - 1] = $newContent
                    $fileContent | Set-Content $Path

                } else {
                    # if we did not find the comment, signal that it was not successful
                    Write-Warning "$Property comment not found"
                }
            } else {
                throw "$Path is not a valid path"
            }
        }

        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }

}
#endregion source\stitch\public\Manifest\ConvertFrom-CommentedProperty.ps1 61
#region source\stitch\public\Manifest\Get-ModuleExtension.ps1 2
function Get-ModuleExtension {
    <#
    .SYNOPSIS
        Find modules with the `Extension` key in the manifest
    .NOTES
        This function was pulled from the Plaster Source at commit #d048667
    #>

    [CmdletBinding()]
    param(
        [string]
        $ModuleName,

        [Version]
        $ModuleVersion,

        [Switch]
        $ListAvailable
    )

    #Only get the latest version of each module
    $modules = Get-Module -ListAvailable
    if (!$ListAvailable) {
        $modules = $modules |
        Group-Object Name |
        Foreach-Object {
            $_.group |
            Sort-Object Version |
            Select-Object -Last 1
        }
    }

    Write-Verbose "`nFound $($modules.Length) installed modules to scan for extensions."

    function ParseVersion($versionString) {
        $parsedVersion = $null

        if ($versionString) {
            # We're targeting Semantic Versioning 2.0 so make sure the version has
            # at least 3 components (X.X.X). This logic ensures that the "patch"
            # (third) component has been specified.
            $versionParts = $versionString.Split('.');
            if ($versionParts.Length -lt 3) {
                $versionString = "$versionString.0"
            }

            if ($PSVersionTable.PSEdition -eq "Core") {
                $parsedVersion = New-Object -TypeName "System.Management.Automation.SemanticVersion" -ArgumentList $versionString
            } else {
                $parsedVersion = New-Object -TypeName "System.Version" -ArgumentList $versionString
            }
        }

        return $parsedVersion
    }

    foreach ($module in $modules) {
        if ($module.PrivateData -and
            $module.PrivateData.PSData -and
            $module.PrivateData.PSData.Extensions) {

            Write-Verbose "Found module with extensions: $($module.Name)"

            foreach ($extension in $module.PrivateData.PSData.Extensions) {

                Write-Verbose "Comparing against module extension: $($extension.Module)"

                $minimumVersion = ParseVersion $extension.MinimumVersion
                $maximumVersion = ParseVersion $extension.MaximumVersion

                if (($extension.Module -eq $ModuleName) -and
                    (!$minimumVersion -or $ModuleVersion -ge $minimumVersion) -and
                    (!$maximumVersion -or $ModuleVersion -le $maximumVersion)) {
                    # Return a new object with the extension information
                    [PSCustomObject]@{
                        Module         = $module
                        MinimumVersion = $minimumVersion
                        MaximumVersion = $maximumVersion
                        Details        = $extension.Details
                    }
                }
            }
        }
    }
}
#endregion source\stitch\public\Manifest\Get-ModuleExtension.ps1 85
#region source\stitch\public\Manifest\Test-CommentedProperty.ps1 1
function Test-CommentedProperty {
    <#
    .SYNOPSIS
        Test if the given property is commented in the given manifest
    .EXAMPLE
        $manifest | Test-CommentedProperty 'ReleaseNotes'
    #>

    [CmdletBinding()]
    param(
        # Specifies a path to one or more locations.
        [Parameter(
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string[]]$Path,

        # The item to uncomment
        [Parameter(
            Position = 0
        )]
        [Alias('PropertyName')]
        [string]$Property

    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        if ($PSBoundParameters.ContainsKey('Path')) {
            if (Test-Path $Path) {
                $commentToken = $Path | Find-ParseToken -Type Comment -Pattern "^\s*#\s*$Property\s+=.*$" | Select-Object -First 1
                $null -ne $commentToken | Write-Output
            }
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Manifest\Test-CommentedProperty.ps1 42
#region source\stitch\public\Manifest\Update-ManifestField.ps1 2
function Update-ManifestField {
    [CmdletBinding()]
    param(
        # Specifies a path to a manifest file
        [Parameter(
        Position = 2,
        ValueFromPipeline,
        ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string]$Path,

        # Field in the Manifest to update
        [Parameter(
            Mandatory,
            Position = 0
        )]
        [string]$PropertyName,

        # List of strings to add to the field
        [Parameter(
            Mandatory,
            Position = 1
        )]
        [string[]]$Value
    )

    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        try {
            Write-Debug "Loading manifest $Path"
            $manifestItem = Get-Item $Path
            $manifestObject = Import-Psd $manifestItem.FullName
        }
        catch {
            throw "Cannot load $($Path)`n$_"
        }

        $options = $PSBoundParameters
        $null = $options.Remove('Name')

        if ($manifestObject.ContainsKey($PropertyName)) {
            #-------------------------------------------------------------------------------
            #region Field exists
            Write-Debug " - Manifest has a $PropertyName field. Updating"
            try {
                Update-Metadata @options
            }
            catch {
                throw "Cannot update $PropertyName in $Path`n$_"
            }
            #endregion Field exists
            #-------------------------------------------------------------------------------
        } else {
            #-------------------------------------------------------------------------------
            #region Commented
            Write-Debug "Manifest does not have $PropertyName field. Looking for it in comments"
            $fieldToken = $manifestItem | Find-ParseToken $PropertyName Comment
            if ($null -ne $fieldToken) {
                Write-Debug " - Found comment"
                try {
                    $manifestItem | ConvertFrom-CommentedProperty -Property $PropertyName
                    Update-Metadata @options
                }
                catch {
                    throw "Cannot update $PropertyName in $Path`n$_"
                }

                #endregion Commented
                #-------------------------------------------------------------------------------
            } else {
                #-------------------------------------------------------------------------------
                #region Field missing
                #! Update-ModuleManifest is not really the best option for editing the psd1, because
                #! it does a poor job of formatting "proper" arrays, and it doesn't deal with "non-standard"
                #! fields very well. However, if the field is missing from the file, it is better to use
                #! Update-ModuleManifest than to clobber the comments and formatting ...

                Write-Debug "Could not find $PropertyName in Manifest. Calling Update-ModuleManifest"
                $null = $options.Clear()
                $options = @{
                    Path = $Path
                    $PropertyName = $Value
                }
                try {
                    Update-ModuleManifest @options
                } catch {
                    throw "Cannot update $PropertyName in $Path`n$_"
                }
            #endregion Field missing
            #-------------------------------------------------------------------------------

            }
        }

        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Manifest\Update-ManifestField.ps1 105
#region source\stitch\public\Notification\Invoke-BuildNotification.ps1 2
function Invoke-BuildNotification {
    <#
    .SYNOPSIS
        Display a Toast notification for a completed build
    .EXAMPLE
        Invoke-BuildNotification -LogFile .\out\logs\build-20230525T2051223032Z.log -Status Passed
    #>

    [CmdletBinding()]
    param(
        # The text to add to the notification
        [Parameter(
        )]
        [string]$Text,

        # Build status
        [Parameter(
        )]
        [ValidateSet('Passed', 'Failed', 'Unknown')]
        [string]$Status,

        # Path to the log file
        [Parameter(
        )]
        [string]$LogFile
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $appImage = (Join-Path (Get-ModulePath) "spool-of-thread_1f9f5.png")
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"

        if (-not ($PSBoundParameters.ContainsKey('Text'))) {
            $Text = "Build Complete"
        }

        if ($PSBoundParameters.ContainsKey('Status')) {
            if ($Status -like 'Passed') {
                $Text = "`u{2705} $Text"
            } elseif ($Status -like 'Failed') {
                $Text = "`u{1f6a8} $Text"
            }
        } else {
            $Text = "`u{2754} $Text"
        }

        $toastOptions = @{
            Text = $Text
            AppLogo = $appImage
        }

        if ($PSBoundParameters.ContainsKey('LogFile')) {
            if (Test-Path $LogFile) {
                $logItem = Get-Item $LogFile
                $btnOptions = @{
                    Content = "Build Log"
                    ActivationType = 'Protocol'
                    Arguments = $logItem.FullName
                }

                $logButton = New-BTButton @btnOptions
                $toastOptions.Text = @($Text, "View the log file")
                $toastOptions.Button = $logButton
            }
        }


        New-BurntToastNotification @toastOptions


        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Notification\Invoke-BuildNotification.ps1 77
#region source\stitch\public\Path\Confirm-Path.ps1 2
function Confirm-Path {
    <#
    .SYNOPSIS
        Tests if the directory exists and if it does not, creates it.
    #>

    [OutputType([bool])]
    [CmdletBinding()]
    param(
        # The path to confirm
        [Parameter(
        Position = 0,
        ValueFromPipeline,
        ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string[]]$Path,

        # The type of item to confirm
        [Parameter(
        )]
        [ValidateSet('Directory', 'File', 'SymbolicLink', 'Junction', 'HardLink')]
        [string]$ItemType = 'Directory'
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        if (Test-Path $Path) {
            Write-Debug "Path exists"
            $true
        } else {
            try {
                Write-Debug "Checking if the directory exists"
                $directory = $Path | Split-Path -Parent
                if (Test-Path $directory) {
                    Write-Debug " - The directory $directory exists"
                } else {
                    $null = New-Item $directory -Force -ItemType Directory
                }
                Write-Debug "Creating $ItemType $Path"
                $null = New-Item -Path $Path -ItemType $ItemType -Force
                Write-Debug "Now confirming $Path exists"
                if (Test-Path $Path) {
                    $true
                } else {
                    $false
                }
            }
            catch {
                throw "There was an error confirming $Path`n$_"
            }
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Path\Confirm-Path.ps1 60
#region source\stitch\public\Path\Find-BuildConfigurationRootDirectory.ps1 2
function Find-BuildConfigurationRootDirectory {
    <#
    .SYNOPSIS
        Find the build configuration root directory for this project
    .EXAMPLE
        Find-BuildConfigurationRootDirectory -Path $BuildRoot
    .EXAMPLE
        $BuildRoot | Find-BuildConfigurationRootDirectory
    .NOTES
        `Find-BuildConfigurationRootDirectory` looks in the current directory of the caller if no Path is given
    #>

    [OutputType([System.IO.DirectoryInfo])]
    [CmdletBinding()]
    param(
        # Specifies a path to a location to look for the build configuration root
        [Parameter(
        Position = 0,
        ValueFromPipeline,
        ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string]$Path
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"

        #TODO: A good example of what would be in the module's (PoshCode) Configuration if we used it
        $possibleRoots = @(
            '.build',
            '.stitch'
        )
        $buildConfigRoot = $null
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        if (-not($PSBoundParameters.ContainsKey('Path'))) {
            $Path = Get-Location
        }
        :path foreach ($possibleRootPath in $Path) {
            :root foreach ($possibleRoot in $possibleRoots) {
                $possiblePath =  (Join-Path $possibleRootPath $possibleRoot)
                if (Test-Path $possiblePath) {
                    $possiblePathItem = (Get-Item $possiblePath)
                    if ($possiblePathItem.PSIsContainer) {
                        $buildConfigRoot = $possiblePathItem
                    } else {
                        $buildConfigRoot = (Get-Item ($possiblePathItem | Split-Path -Parent))
                    }
                    Write-Debug "Found build configuration root directory 'buildConfigRoot'"
                    break path
                }
            }
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        $buildConfigRoot
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Path\Find-BuildConfigurationRootDirectory.ps1 61
#region source\stitch\public\Path\Find-BuildProfileRootDirectory.ps1 2
function Find-BuildProfileRootDirectory {
    <#
    .SYNOPSIS
        Find the directory that has the profiles defined
    #>

    [CmdletBinding()]
    param(
        # Specifies a path to a location that contains Build Profiles (This should be BuildConfigPath)
        [Parameter(
            Position = 0,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string[]]$Path
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $possibleProfileDirectories = @(
            'profiles',
            'profile',
            'runbooks'
        )
        $profileDirectory = $null
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        if (-not($PSBoundParameters.ContainsKey('Path'))) {
            $possibleBuildConfigRoot += $PSCmdlet.GetVariableValue('BuildConfigRoot')
            if ([string]::IsNullorEmpty($possibleBuildConfigRoot)) {
                $Path += Get-Location
            } else {
                $Path += $possibleBuildConfigRoot
            }
        }

        :root foreach ($possibleRootPath in $Path) {
            :profile foreach ($possibleProfileDirectory in $possibleProfileDirectories) {
                $possibleProfilePath = (Join-Path $possibleRootPath $possibleProfileDirectory)
                if (Test-Path $possibleProfilePath) {
                    $possiblePathItem = (Get-Item $possibleProfilePath)
                    if ($possiblePathItem.PSIsContainer) {
                        $profileDirectory = $possibleProfilePath
                    } else {
                        $profileDirectory = $possibleProfilePath | Split-Path -Parent
                    }
                    Write-Debug "Found profile root directory '$profileDirectory'"
                    break root
                }
            }
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        $profileDirectory
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Path\Find-BuildProfileRootDirectory.ps1 59
#region source\stitch\public\Path\Find-BuildRunBook.ps1 2
function Find-BuildRunBook {
    [CmdletBinding()]
    param(
        # Specifies a path to one or more locations.
        [Parameter(
        Position = 0,
        ValueFromPipeline,
        ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string[]]$Path
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $possibleRunbookFilters = @(
            "*runbook.ps1"
        )
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        :path foreach ($location in $Path) {
            :filter foreach ($possibleRunbookFilter in $possibleRunbookFilters) {
                $options = @{
                    Path = $location
                    Recurse = $true
                    Filter = $possibleRunbookFilter
                    File = $true
                }
                Get-Childitem @options
            }
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Path\Find-BuildRunBook.ps1 38
#region source\stitch\public\Path\Find-InvokeBuildScript.ps1 1
function Find-InvokeBuildScript {
    <#
    .SYNOPSIS
        Find all "build script" files. These are files that contain tasks to be executed by Invoke-Build
    .LINK
        Find-InvokeBuildTaskFile
    #>

    [CmdletBinding()]
    param(
        # Specifies a path to one or more locations to look for build scripts.
        [Parameter(
        Position = 0,
        ValueFromPipeline,
        ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string[]]$Path
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $buildScriptPattern = "*.build.ps1"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        foreach ($location in $Path) {
            if (Test-Path $location) {
                $options = @{
                    Path = $location
                    Recurse = $true
                    Filter = $buildScriptPattern
                }
                Get-ChildItem @options
            }
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Path\Find-InvokeBuildScript.ps1 40
#region source\stitch\public\Path\Find-InvokeBuildTaskFile.ps1 2
function Find-InvokeBuildTaskFile {
    <#
    .SYNOPSIS
        Find all "task type" files. These are files that contain "extensions" to the task types. They define a
        function that creates tasks.
    .LINK
        Find-InvokeBuildScript
    #>

    [CmdletBinding()]
    param(
        # Specifies a path to one or more locations to look for task files.
        [Parameter(
        Position = 0,
        ValueFromPipeline,
        ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string[]]$Path
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $taskFilePattern = "*.task.ps1"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        foreach ($location in $Path) {
            if (Test-Path $location) {
                $options = @{
                    Path = $location
                    Recurse = $true
                    Filter = $taskFilePattern
                }
                Get-ChildItem @options
            }
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Path\Find-InvokeBuildTaskFile.ps1 42
#region source\stitch\public\Path\Find-LocalUserStitchDirectory.ps1 2
function Find-LocalUserStitchDirectory {
    [CmdletBinding()]
    param(
        # Specifies a path to one or more locations to look for the users local stitch directory
        [Parameter(
        Position = 0,
        ValueFromPipeline,
        ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string[]]$Path
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $possibleRootDirectories = @(
            $env:USERPROFILE,
            $env:HOME,
            $env:LOCALAPPDATA,
            $env:APPDATA
        )

        $possibleStitchDirectories = @(
            '.stitch'
        )

        $userStitchDirectory = $null
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        if (-not($PSBoundParameters.ContainsKey('Path'))) {
            $Path = $possibleRootDirectories
        }
        #! We only need to search the 'possibleRootDirectories' if a Path was not given
        :root foreach ($possibleRootDirectory in $Path) {
            :stitch foreach ($possibleStitchDirectory in $possibleStitchDirectories) {
                if  ((-not ([string]::IsNullorEmpty($possibleRootDirectory))) -and
                     (-not ([string]::IsNullorEmpty($possibleStitchDirectory)))) {

                    $possiblePath = (Join-Path $possibleRootDirectory $possibleStitchDirectory)
                        if (Test-Path $possiblePath) {
                            $possiblePathItem = (Get-Item $possiblePath)
                            if ($possiblePathItem.PSIsContainer) {
                                $userStitchDirectory = $possiblePath
                            } else {
                                $userStitchDirectory = $possiblePath | Split-Path -Parent
                            }
                        Write-Debug "Local user stitch directory found at $userStitchDirectory"
                        break root
                    }
                }
            }
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        $userStitchDirectory
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Path\Find-LocalUserStitchDirectory.ps1 60
#region source\stitch\public\Path\Find-StitchConfigurationFile.ps1 2
function Find-StitchConfigurationFile {
    [CmdletBinding()]
    param(
        # Specifies a path to one or more locations.
        [Parameter(
        Position = 0,
        ValueFromPipeline,
        ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string[]]$Path
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $possibleConfigFileFilters = @(
            'stitch.config.ps1',
            '.config.ps1'
        )
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        :path foreach ($location in $Path) {
            Write-Debug "Looking in $location"
            :filter foreach ($possibleConfigFileFilter in $possibleConfigFileFilters) {
                $options = @{
                    Path = $location
                    Recurse = $true
                    Filter = $possibleConfigFileFilter
                    File = $true
                }
                $result = Get-Childitem @options | Select-Object -First 1
                if ($null -ne $result) {
                    $result | Write-Output
                    continue path
                }
            }
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Path\Find-StitchConfigurationFile.ps1 44
#region source\stitch\public\Path\Get-ModulePath.ps1 2
function Get-ModulePath {
    [CmdletBinding()]
    param(
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $callStack = Get-PSCallStack
        $caller = $callStack[1]

        $caller.InvocationInfo.MyCommand.Module.ModuleBase

        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Path\Get-ModulePath.ps1 21
#region source\stitch\public\Path\Resolve-ProjectRoot.ps1 2
function Resolve-ProjectRoot {
    <#
    .SYNOPSIS
        Find the root of the current project
    .DESCRIPTION
        Resolve-ProjectRoot will recurse directories toward the root folder looking for a directory that passes
        `Test-ProjectRoot`, unless `$BuildRoot` is already set
 
    .LINK
        Test-ProjectRoot
    #>

    [CmdletBinding()]
    param(
        # Optionally set the starting path to search from
        [Parameter(
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string]$Path = (Get-Location).ToString(),

        # Optionally limit the number of levels to seach
        [Parameter()]
        [int]$Depth = 8,

        # Powershell Data File with defaults
        [Parameter(
        )]
        [string]$Defaults,

        # Default Source directory
        [Parameter(
        )]
        [string]$Source = '.\source',

        # Default Tests directory
        [Parameter(
        )]
        [string]$Tests = '.\tests',

        # Default Staging directory
        [Parameter(
        )]
        [string]$Staging = '.\stage',

        # Default Artifact directory
        [Parameter(
        )]
        [string]$Artifact = '.\out',

        # Default Docs directory
        [Parameter(
        )]
        [string]$Docs = '.\docs'
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $level = 1
        $originalLocation = $Path | Get-Item
        $currentLocation = $originalLocation
        $driveRoot = $currentLocation.Root


        Write-Debug "Current location: $($currentLocation.FullName)"
        Write-Debug "Current root: $($driveRoot.FullName)"
    }
    process {
        $rootReached = $false
        if ($null -ne $BuildRoot) {
            Write-Debug "BuildRoot is set, using that"
            $BuildRoot | Write-Output
            break
        }
        #TODO: Here we could look for .build.ps1 as the root, or perhaps tie it to a repository by looking for .git/
        :location do {
            if ($null -ne $currentLocation) {
                $null = $PSBoundParameters.Remove('Path')
                if (Test-ProjectRoot @PSBoundParameters -Path $currentLocation.FullName) {
                    $rootReached = $true
                    Write-Debug "Project Root found : $($currentLocation.FullName)"
                    $currentLocation.FullName | Write-Output
                    break location
                } elseif ($level -eq $Depth) {
                    $rootReached = $true
                    throw "Could not find project root in $Depth levels"
                    break location
                } elseif ($currentLocation -like $driveRoot) {
                    $rootReached = $true
                    throw "$driveRoot reached looking for project root"
                    break location
                } else {
                    Write-Debug " Level: $level - $($currentLocation.Name) is not the project root"
                }
            } else {
                $rootReached = $true
            }
            Write-Debug "Setting current location to Parent"
            $currentLocation = $currentLocation.Parent
            $level++
        } until ($rootReached)
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Path\Resolve-ProjectRoot.ps1 106
#region source\stitch\public\Path\Test-PathIsIn.ps1 2
function Test-PathIsIn {
    <#
    .SYNOPSIS
        Confirm if the given path is within the other
    .DESCRIPTION
        `Test-PathIsIn` checks if the given path (-Path) is a subdirectory of the other (-Parent)
    .EXAMPLE
        Test-PathIsIn "C:\Windows" -Path "C:\Windows\System32\"
 
    .EXAMPLE
        "C:\Windows\System32" | Test-PathIsIn "C:\Windows"
    #>

    [OutputType([System.Boolean])]
    [CmdletBinding()]
    param(
        # The path to test (the subdirectory)
        [Parameter(
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string]$Path,

        # The path to test (the subdirectory)
        [Parameter(
            Position = 0
        )]
        [string]$Parent,

        # Compare paths using case sensitivity
        [Parameter(
        )]
        [switch]$CaseSensitive
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        try {
            Write-Debug "Resolving given Path $Path"
            $childItem = Get-Item (Resolve-Path $Path)

            Write-Debug "Resolving given Parent $Parent"
            $parentItem = Get-Item (Resolve-Path $Parent)

            if ($CaseSensitive) {
                Write-Debug "Matching case-sensitive"
                $parentPath = $parentItem.FullName
                $childPath = $childItem.FullName
            } else {
                Write-Debug "Matching"
                $parentPath = $parentItem.FullName.ToLowerInvariant()
                $childPath = $childItem.FullName.ToLowerInvariant()
            }

            Write-Verbose "Testing if '$childPath' is in '$parentPath'"

            # early test using string comparison
            #! note: will return a false positive for directories with partial match like
            #! c:\windows\system , c:\windows\system32
            Write-Debug "Does '$childPath' start with '$parentPath'"
            if (-not($childPath.StartsWith($parentPath))) {
                Write-Debug " - Yes. Return False"
                return $false
            } else {
                $childRoot = $childItem.Root
                $parentRoot = $parentItem.Root
                Write-Debug " - Yes. Checking path roots '$childRoot' and '$parentRoot'"

                # they /should/ be equal if we made it here
                if ($parentRoot -notlike $childRoot) {
                    return $false
                }

                $childPathParts = $childPath -split [regex]::Escape([IO.Path]::DirectorySeparatorChar)
                $depth = $childPathParts.Count
                $currentPath = $childItem
                $parentFound = $false
                :depth foreach ($level in 1..($depth - 1)) {
                    $currentPath = $currentPath.Parent
                    Write-Debug "Testing if $currentPath equals $($parentItem.FullName)"
                    if ($currentPath -like $parentItem.FullName) {
                        Write-Debug " - Parent found"
                        $parentFound = $true
                        break depth
                    }
                }
                if ($parentFound) {
                    Write-Debug " - Parent found. Return True"
                    return $true
                }
                Write-Debug " - Parent not found. Return False"
                return $false

            }
        } catch {
            $PSCmdlet.ThrowTerminatingError($_)
        }
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }


}
#endregion source\stitch\public\Path\Test-PathIsIn.ps1 107
#region source\stitch\public\Path\Test-ProjectRoot.ps1 2
function Test-ProjectRoot {
    <#
    .SYNOPSIS
        Test if the given directory is the root directory of a project
    .DESCRIPTION
        `Test-ProjectRoot` looks for "typical" project directories in the given -Path and returns true if at least
        two of them exist.
 
        Typical project directories are:
        - A source directory (this may be controlled by the variable $Source)
        - A staging directory (the variable $Staging)
        - A tests directory (the variable $Tests)
        - A artifact/output directory (the variable $Artifact)
        - A documentation directory (the variable $Docs)
    .EXAMPLE
        Test-ProjectRoot
 
        Without a -Path, tests the current directory for default project directories
    .EXAMPLE
        $projectPath | Test-ProjectRoot
    .NOTES
        Defaults are:
        - Source : .\source
        - Staging : .\stage
        - Tests : .\tests
        - Artifact : .\out
        - Docs : .\docs
 
    #>

    [CmdletBinding()]
    param(
        # Optionally give a path to start in
        [Parameter(
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [ValidateScript(
            {
                if (-not($_ | Test-Path)) {
                    throw "$_ does not exist"
                }
                return $true
            }
        )]
        [Alias('PSPath')]
        [string]$Path = (Get-Location).ToString(),

        # Powershell Data File with defaults
        [Parameter(
        )]
        [string]$Defaults,

        # Default Source directory
        [Parameter(
        )]
        [string]$Source = '.\source',

        # Default Tests directory
        [Parameter(
        )]
        [string]$Tests = '.\tests',

        # Default Staging directory
        [Parameter(
        )]
        [string]$Staging = '.\stage',

        # Default Artifact directory
        [Parameter(
        )]
        [string]$Artifact = '.\out',

        # Default Docs directory
        [Parameter(
        )]
        [string]$Docs = '.\docs'
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        #! How many default directories must be present to be considered true
        $DEFAULTS_REQUIRED = 2

        $FAILSAFE_DEFAULTS = @{
            Source   = $Source
            Tests    = $Tests
            Staging  = $Staging
            Artifact = $Artifact
            Docs     = $Docs
        }

        if ($PSBoundParameters.ContainsKey('Defaults')) {
            if (Test-Path $Defaults) {
                Write-Debug "Importing defaults from $Defaults"
                $defaultFolders = Import-PowerShellDataFile $Defaults
            }
        } else {
            Write-Debug 'No defaults file found using internal defaults'
            $defaultFolders = $FAILSAFE_DEFAULTS
        }
        Write-Debug "Default Folders are:"
        foreach ($key in $defaultFolders.Keys) {
            Write-Debug (" - {0,-16} => {1}" -f $key, $defaultFolders[$key])
        }
    }
    process {
        Write-Debug "Testing against default project directories in $Path"
        $defaultsInDirectory = 0
        foreach ($key in $defaultFolders.Keys) {
            Write-Debug "Checking for $key variable. Defaults found so far $defaultsInDirectory"
            $pathVariable = Get-Variable $key -ValueOnly -ErrorAction SilentlyContinue
            Write-Debug " - The path we are looking for is $pathVariable"
            if ($null -ne $pathVariable) {
                if ([system.io.path]::IsPathFullyQualified($pathVariable)) {
                    Write-Debug " - found $pathVariable fully qualified"
                    $pathToTest = $pathVariable
                } else {
                    $pathToTest = (Join-Path $Path $pathVariable)
                }
            } else {
                throw "No value given for `$$key"
            }

            Write-Debug "Testing if $pathToTest is present"
            if (Test-Path $pathToTest) {
                Write-Debug ' - It was found'
                $defaultsInDirectory += 1
            } else {
                Write-Debug ' - It was NOT found'
            }
        }
    }
    end {
        Write-Debug "$defaultsInDirectory found $DEFAULTS_REQUIRED needed to pass"
        $defaultsInDirectory -ge $DEFAULTS_REQUIRED | Write-Output

        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Path\Test-ProjectRoot.ps1 139
#region source\stitch\public\Project\Get-ProjectPath.ps1 1
function Get-ProjectPath {
    <#
    .SYNOPSIS
        Retrieve the paths to the major project components. (Source, Tests, Docs, Artifacts, Staging)
    #>

    [CmdletBinding()]
    param(
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)'"
        $stitchPathFiles = @(
            '.stitch.config.psd1',
            '.stitch.psd1',
            'stitch.config.psd1'
            )
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $root = Resolve-ProjectRoot
        if ($null -ne $root) {
            $possibleBuildRoot = $PSCmdlet.GetVariableValue('BuildRoot')
            if (-not ([string]::IsNullorEmpty($possibleBuildRoot))) {
                $root = $possibleBuildRoot
            } else {
                $root = Get-Location
            }
        }
        Write-Verbose "Looking for path config file in $root"
        $pathConfigFiles = (Get-ChildItem -Path "$root/*.psd1" -Include $stitchPathFiles)
        if ($pathConfigFiles.Count -gt 0) {
            Write-Debug ('Found ' + ($pathConfigFiles.Name -join "`n"))
            $pathConfigFile = $pathConfigFiles[0]
        }

        if ($null -ne $pathConfigFile) {
            Write-Verbose " - found $pathConfigFile"
            try {
                $config = Import-Psd $pathConfigFile
                $resolved = @{}
                foreach ($key in $config.Keys) {
                    $resolved[$key] = (Resolve-Path $config[$key])
                }
            } catch {
                $PSCmdlet.ThrowTerminatingError($_)
            }
            [PSCustomObject]$resolved | Write-Output
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Project\Get-ProjectPath.ps1 53
#region source\stitch\public\Project\Get-ProjectVersionInfo.ps1 1
function Get-ProjectVersionInfo {
    [CmdletBinding()]
    param(
        # Specifies a path to one or more locations.
        [Parameter(
        Position = 0,
        ValueFromPipeline,
        ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string[]]$Path
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        if (-not($PSBoundParameters.ContainsKey('Path'))) {
            $Path = Get-Location
        }
        #TODO: We could also parse the version field from the root module's manifest
        Write-Debug 'Checking for version information'
        Write-Debug ' - Checking for gitversion utility'
        $gitverCmd = Get-Command dotnet-gitversion.exe -ErrorAction SilentlyContinue

        if ($null -eq $gitverCmd) {
            Write-Information "GitVersion is not installed.`nsee <https://gitversion.net/docs/usage/cli/installation> for details"
            Write-Debug ' - gitversion not found'
            Write-Debug ' - Looking for version.* file'
            $found = Get-ChildItem -Path $Path -Filter 'version.*' -Recurse |
                Sort-Object LastWriteTime |
                    Select-Object -Last 1

            if ($null -ne $found) {
                Write-Verbose "Using $found for version info"
                Write-Debug " - Found $($found.FullName)"
                switch -Regex ($found.extension) {
                    'psd1' { $versionInfo = Import-Psd $found }
                    'json' { $versionInfo = (Get-Content $found | ConvertFrom-Json) }
                    'y(a)?ml' { $versionInfo = (Get-Content $found | ConvertFrom-Yaml) }
                    Default { Write-Information "$($found.Name) found but no converter for $($found.extension) is set" }
                }
            } else {
                Write-Debug " - No version files found in $Path"
                $buildInfo = $PSCmdlet.GetVariableValue('BuildInfo')

                if ($null -ne $buildInfo) {
                    switch ($buildInfo.Modules.Keys.Count) {
                        0 {
                            throw "Could not find any modules in project to get version info"
                        }
                        1 {
                            $buildInfo.Modules[0].ModuleVersion | Write-Output
                        }
                        default {
                            Write-Verbose "Multiple module versions found using highest version"
                            $buildInfo.Modules.ModuleVersion | Sort-Object -Descending |
                                Select-Object -First 1 | Write-Output
                        }
                    }
                }
            }

        } else {
            Write-Verbose "Using gitversion for version info"
            $gitVersionCommandInfo = & $gitverCmd @('-?')

            Write-Debug ' - gitversion found. Getting version info'
            $gitVersionCommandInfo | Write-Debug
            $gitVersionOutput = & $gitverCmd @( '-output', 'json')
            if ([string]::IsNullorEmpty($gitVersionOutput)) {
                Write-Warning "No output from gitversion"
            } else {
                Write-Debug "Version info: $gitVersionOutput"
                try {
                    $versionInfo = $gitVersionOutput | ConvertFrom-Json
                } catch {
                    throw "Could not parse json:`n$gitVersionOutput`n$_"
                }
            }
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        $versionInfo
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Project\Get-ProjectVersionInfo.ps1 88
#region source\stitch\public\Project\Initialize-StitchProject.ps1 4
function Initialize-StitchProject {

    [Alias('Institchilize')]
    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact = 'high'
    )]
    [SuppressMessage('PSAvoidUsingWriteHost', '', Justification='Output of write operation should not be redirected')]
    param(
        # The directory to initialize the build tool in.
        # Defaults to the current directory.
        [Parameter(
        )]
        [string]$Destination,

        # Overwrite existing files
        [Parameter(
        )]
        [switch]$Force,

        # Do not output any status to the console
        [Parameter(
        )]
        [switch]$Quiet
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"

    }
    process {
        if ([string]::IsNullorEmpty($Destination)) {
            Write-Debug "Setting Destination to current directory"
            $Destination = (Get-Location).Path
        }
        $possibleBuildConfigRoot = $Destination | Find-BuildConfigurationRootDirectory
        if (-not ([string]::IsNullorEmpty($possibleBuildConfigRoot))) {
            $buildConfigDir = $possibleBuildConfigRoot
        } else {
            $buildConfigDefaultDir = '.build'
        }
        #-------------------------------------------------------------------------------
        #region Gather info

        if (-not($Quiet)) {
            Write-StitchLogo -Size 'large'
        }

        New-StitchPathConfigurationFile -Force:$Force

        if (-not ([string]::IsNullorEmpty($buildConfigDir))) {
            "Found your build configuration directory '$(Resolve-Path $buildConfigDir -Relative)'"
        } else {
            $prompt = ( -join @(
                'What is the name of your build configuration directory? ',
                $PSStyle.Foreground.BrightBlack,
                " ( $buildConfigDefaultDir )",
                $PSStyle.Reset
                )
                )

            $ans = Read-Host $prompt
            if ([string]::IsNullorEmpty($ans)) {
                $ans = $buildConfigDefaultDir
            }
            $buildConfigDir = (Join-Path $Destination $ans)
        }

        #endregion Gather info
        #-------------------------------------------------------------------------------

        #-------------------------------------------------------------------------------
        #region Create directories
        Write-Debug "Create directories if they do not exist"
        Write-Debug " - Looking for $buildConfigDir"
        if (-not(Test-Path $buildConfigDir)) {
            try {
                '{0} does not exist. {1}Creating{2}' -f $buildConfigDir, $PSStyle.Foreground.Green, $PSStyle.Reset
                $null = mkdir $buildConfigDir -Force
            } catch {
                throw "Could not create Build config directory $BuildConfigDir`n$_"
            }
        }
        $profileRoot = $buildConfigDir | Find-BuildProfileRootDirectory
        if ($null -eq $profileRoot) {
            $profileRoot = (Join-Path $buildConfigDir 'profiles')
            try {
                '{0} does not exist. {1}Creating{2}' -f $profileRoot, $PSStyle.Foreground.Green, $PSStyle.Reset
                $null = mkdir $profileRoot -Force
            } catch {
                throw "Could not create build profile directory $profileRoot`n$_"
            }
        }
        if (-not (Test-Path (Join-Path $profileRoot 'default'))) {
            '{0} does not exist. {1}Creating{2}' -f 'default profile', $PSStyle.Foreground.Green, $PSStyle.Reset
        }
        $profileRoot | New-StitchBuildProfile -Name 'default' -Force:$Force
        Get-ChildItem (Join-Path $profileRoot 'default') -Filter "*.ps1" | Foreach-Object {
            $_ | Format-File 'CodeFormattingOTBS'
        }

        if (-not (Test-Path (Join-Path $Destination '.build.ps1'))) {
            '{0} does not exist. {1}Creating{2}' -f 'build runner', $PSStyle.Foreground.Green, $PSStyle.Reset
        }
        $Destination | New-StitchBuildRunner -Force:$Force
        Get-ChildItem $Destination -Filter ".build.ps1" | Format-File 'CodeFormattingOTBS'

        #endregion Create directories
        #-------------------------------------------------------------------------------
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Project\Initialize-StitchProject.ps1 116
#region source\stitch\public\Project\New-StitchBuildProfile.ps1 4
function New-StitchBuildProfile {
    [SuppressMessage('PSUseShouldProcessForStateChangingFunctions', '',
        Justification = 'File creation methods have their own ShouldProcess')]
    [CmdletBinding()]
    param(
        # The name of the profile to create
        [Parameter(
            Mandatory,
            Position = 0
        )]
        [string]$Name,

        # Profile path in the build config path
        [Parameter(
            Position = 1,
            ValueFromPipeline
        )]
        [string]$ProfileRoot,

        # Overwrite the profile if it exists
        [Parameter(
        )]
        [switch]$Force

    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        if (-not ($PSBoundParameters.ContainsKey('ProfileRoot'))) {
            $possibleProfileRoot = Find-BuildProfileRootDirectory
            if ($null -ne $possibleProfileRoot) {
                $ProfileRoot = $possibleProfileRoot
                Remove-Variable $possibleProfileRoot -ErrorAction SilentlyContinue
            } else {
                throw 'Could not find the build profile root directory. Use -ProfileRoot'
            }
        }
        $newProfileDirectory = (Join-Path $ProfileRoot $Name)
        if ((Test-Path $newProfileDirectory) -and
            (-not ($Force))) {
            throw "Profile '$Name' already exists at $newProfileDirectory. Use -Force to Overwrite"
        } else {
            try {
                Write-Debug 'Creating directory'
                $null = mkdir $newProfileDirectory -Force
                Write-Debug 'Creating runbook'
                $newProfileDirectory | New-StitchRunBook -Force:$Force
                Write-Debug 'Creating configuration file'
                $newProfileDirectory | New-StitchConfigurationFile -Force:$Force
            } catch {
                throw "Could not create new build profile '$Name' in '$newProfileDirectory'`n$_"
            }
            #TODO: if we fail to create a file, should we remove the folder in a finally block?
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Project\New-StitchBuildProfile.ps1 65
#region source\stitch\public\Project\New-StitchBuildRunner.ps1 1
function New-StitchBuildRunner {
    <#
    .SYNOPSIS
        Create the main stitch build script
    .EXAMPLE
        New-StitchBuildRunner $BuildRoot
 
        Creates the file $BuildRoot\.build.ps1
    #>

    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact = 'Low'
    )]
    param(
        # Specifies a path to the folder where the runbook should be created
        [Parameter(
            Position = 0,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string]$Path,

        # The name of the main build script.
        [Parameter(
        )]
        [string]$Name,

        # Overwrite the file if it exists
        [Parameter(
        )]
        [switch]$Force
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $template = Get-StitchTemplate -Type 'install' -Name '.build.ps1'


        if ($null -ne $template) {
            $template.Destination = $Path
            if ($PSBoundParameters.ContainsKey('Name')) {
                $template.Name = $Name
            }

            if (Test-Path $template.Target) {
                if ($Force) {
                    if ($PSCmdlet.ShouldProcess($template.Target, 'Overwrite file')) {
                        $template | Invoke-StitchTemplate -Force
                    }
                } else {
                    throw "$($template.Target) already exists. Use -Force to overwrite"
                }
            } else {
                $template | Invoke-StitchTemplate
            }
        } else {
            throw 'Could not find the stitch build script file template'
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Project\New-StitchBuildRunner.ps1 67
#region source\stitch\public\Project\New-StitchConfigurationFile.ps1 1
function New-StitchConfigurationFile {
    <#
    .SYNOPSIS
        Create a configuration in the folder specified in Path.
    .EXAMPLE
        New-StitchConfigurationFile $BuildRoot\.stitch\profiles\site
 
        Creates the file $BuildRoot\.stitch\profiles\site\stitch.config.ps1
    #>

    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact = 'Low'
    )]
    param(
        # Specifies a path to the folder where the runbook should be created
        [Parameter(
            Position = 0,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string]$Path,

        # The name of the configuration file.
        [Parameter(
        )]
        [string]$Name,

        # Overwrite the file if it exists
        [Parameter(
        )]
        [switch]$Force
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $template = Get-StitchTemplate -Type 'install' -Name '.config.ps1'


        if ($null -ne $template) {
            $template.Destination = $Path
            if ($PSBoundParameters.ContainsKey('Name')) {
                $template.Name = $Name
            } else {
                $template.Name = 'stitch.config.ps1'
            }
            if (Test-Path $template.Target) {
                if ($Force) {
                    if ($PSCmdlet.ShouldProcess($template.Target, 'Overwrite file')) {
                        $template | Invoke-StitchTemplate -Force
                    }
                } else {
                    throw "$($template.Target) already exists. Use -Force to overwrite"
                }
            } else {
                $template | Invoke-StitchTemplate
            }
        } else {
            throw 'Could not find the stitch configuration file template'
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Project\New-StitchConfigurationFile.ps1 68
#region source\stitch\public\Project\New-StitchConfigurationPath.ps1 1
function New-StitchConfigurationPath {
    [CmdletBinding()]
    param(
        # Specifies a path to one or more locations.
        [Parameter(
        Position = 1,
        ValueFromPipeline,
        ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string]$Path,

        # The name of the directory. Supports '.build' or '.stitch'
        [Parameter(
        )]
        [ValidateSet('.build', '.stitch')]
        [string]$Name = '.build'
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        if (-not ($PSBoundParameters.ContainsKey('Path'))) {
            $Path = Get-Location
        }
        $buildConfigDir = (Join-Path $Path $Name)
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        Write-Debug 'Create directories if they do not exist'
        Write-Debug " - Looking for $buildConfigDir"
        if (-not(Test-Path $buildConfigDir)) {
            try {
                '{0} does not exist. {1}Creating{2}' -f $buildConfigDir, $PSStyle.Foreground.Green, $PSStyle.Reset
                $null = mkdir $buildConfigDir -Force
            } catch {
                throw "Could not create Build config directory $BuildConfigDir`n$_"
            }
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }

}
#endregion source\stitch\public\Project\New-StitchConfigurationPath.ps1 44
#region source\stitch\public\Project\New-StitchPathConfigurationFile.ps1 2
function New-StitchPathConfigurationFile {
    [CmdletBinding()]
    param(
        # Default Source directory
        [Parameter(
        )]
        [string]$Source,

        # Default Tests directory
        [Parameter(
        )]
        [string]$Tests,

        # Default Staging directory
        [Parameter(
        )]
        [string]$Staging,

        # Default Artifact directory
        [Parameter(
        )]
        [string]$Artifact,

        # Default Docs directory
        [Parameter(
        )]
        [string]$Docs,

        # Do not validate paths
        [Parameter(
        )]
        [switch]$DontValidate,

        # Overwrite an existing file
        [Parameter(
        )]
        [switch]$Force
    )
begin {
    Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    $defaultPathConfigFile = (Join-Path (Get-Location) '.stitch.config.psd1')
    $locations = @{
        Source = @{}
        Tests = @{}
        Staging = @{}
        Artifacts = @{}
        Docs = @{}
    }
}
process {
    Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    foreach ($location in $locations.Keys) {
        if (-not ($PSBoundParameters.ContainsKey($location))) {
            $pathIsSet = $false
            do {
                $ans = Read-Host "The directory where this project's $location is stored: "
                if (-not ($DontValidate)) {
                    $possiblePath = (Join-Path (Get-Location) $ans)
                    if (-not (Test-Path $possiblePath)) {
                        $confirmAnswer = Read-Host "$possiblePath does not exist. Use anyway?"
                        if (([string]::IsNullorEmpty($confirmAnswer)) -or
                            ($confirmAnswer -match '^[yY]')) {
                                $PSBoundParameters[$location] = $ans
                                $pathIsSet = $true # break out of loop for this location
                        }
                    }
                } else {
                    $pathIsSet = $true
                }
            } while (-not ($pathIsSet))
        }
    }

    $pathSettings = $PSBoundParameters

    foreach ($unusedParameter in @('DontValidate', 'Force')) {
        if ($pathSettings.ContainsKey($unusedParameter)) {
            $null = $pathSettings.Remove($unusedParameter)
        }
    }

    if (Test-Path $defaultPathConfigFile) {
        if ($Force) {
            if ($PSCmdlet.ShouldProcess("$defaultPathConfigFile", "Overwrite existing file")) {
                $pathSettings | Export-Psd -Path $defaultPathConfigFile
            }
        }
    }


    Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
}
end {
    Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
}
}
#endregion source\stitch\public\Project\New-StitchPathConfigurationFile.ps1 97
#region source\stitch\public\Project\New-StitchRunBook.ps1 1
function New-StitchRunBook {
    <#
    .SYNOPSIS
        Create a runbook in the folder specified in Path.
    .EXAMPLE
        New-StitchRunBook $BuildRoot\.stitch\profiles\site
 
        Creates the file $BuildRoot\.stitch\profiles\site\runbook.ps1
    #>

    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact = 'Low'
    )]
    param(
        # Specifies a path to the folder where the runbook should be created
        [Parameter(
        Position = 0,
        ValueFromPipeline,
        ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string]$Path,

        # The name of the runbook. Not needed if using profiles
        [Parameter(
        )]
        [string]$Name,

        # Overwrite the file if it exists
        [Parameter(
        )]
        [switch]$Force
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $template = Get-StitchTemplate -Type 'install' -Name 'runbook.ps1'

        $template.Destination = $Path

        if ($null -ne $template) {
            if ($PSBoundParameters.ContainsKey('Name')) {
                $template.Name = $Name
            }
            if (Test-Path $template.Target) {
                if ($Force) {
                    if ($PSCmdlet.ShouldProcess($template.Target, "Overwrite file")) {
                        $template | Invoke-StitchTemplate -Force
                    }
                } else {
                    throw "$($template.Target) already exists. Use -Force to overwrite"
                }
            } else {
                $template | Invoke-StitchTemplate
            }
        } else {
            throw "Could not find the runbook template"
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Project\New-StitchRunBook.ps1 66
#region source\stitch\public\Project\Resolve-ProjectName.ps1 2
function Resolve-ProjectName {
    [CmdletBinding()]
    param(
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        $config = Get-BuildConfiguration
        if ([string]::IsNullorEmpty($config.Project.Name)) {
            Write-Debug "Project name not set in configuration`n trying to resolve project root"
            $root = (Resolve-ProjectRoot).BaseName
        } else {
            Write-Debug "Project Name found in configuration"
            $root = $config.Project.Name
        }
    }
    end {
        $root
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Project\Resolve-ProjectName.ps1 23
#region source\stitch\public\Project\Test-ProjectPath.ps1 2
function Test-ProjectPath {
    [CmdletBinding()]
    param(

    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"

        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Project\Test-ProjectPath.ps1 18
#region source\stitch\public\Project\Write-StitchLogo.ps1 2
function Write-StitchLogo {
    [CmdletBinding()]
    param(
        # Small or large logo
        [Parameter(
        )]
        [ValidateSet('small', 'large')]
        [string]$Size = 'large',

        # Do not print the logo in color
        [Parameter(
        )]
        [switch]$NoColor
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $stitchEmoji = "`u{1f9f5}"

        $stitchLogoSmall = @'
:2: ___ _ _ _ _
:2: / __|| |_ (_)| |_ __ | |_
:2: \__ \| _|| || _|/ _|| \
:2: |___/ \__||_| \__|\__||_||_|
'@



        $stitchLogoLarge = @'
:1:-=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=:0:
:1:=- ________________ =-:0:
:1:-= (________________) xxxxxx xx xxxx xx xx -=:0:
:1:=-:0: :2:(______ ) :1: x x x x x x x x x x =-:0:
:1:-=:0: :2:( _____ ) :1: x xxxx x xxxxx xxxx x xxxxx xxxx x xxx -=:0:
:1:=-:0: :2:( ____ ) :1: x x x x xxxx x x x x x x =-:0:
:1:-=:0: :2:( ____) :1: xxxx x x xxx x x x xxx x xxx x x -=:0:
:1:=-:0: _:2:(____________):1:_ x x x x x x x x x x x x x x =-:0:
:1:-= (________________) xxxxxxx xxxxxx xxxx xxxxxx xxxxxx xxxx xxxx -=:0:
:1:=- =-:0:
:1:-=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=--=-=:0:
'@


    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        if ($Size -like 'small') {
            $logoSource = $stitchLogoSmall
        } else {
            $logoSource = $stitchLogoLarge
        }
        if (-not($NoColor)) {
            $colors = @(
                $PSStyle.Reset,
                $PSStyle.Foreground.FromRgb('#b1a986'),
                $PSStyle.Foreground.FromRgb('#0679d0')
            )
        } else {
            $colors = @(
                '',
                '',
                ''
            )
        }
        $logoOutput = $logoSource
         for ($c = 0; $c -lt $colors.Length; $c++) {
            $logoOutput = $logoOutput -replace ":$($c.ToString()):", $colors[$c]
         }

        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        $logoOutput
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Project\Write-StitchLogo.ps1 74
#region source\stitch\public\SourceInfo\Find-TodoItem.ps1 2
function Find-TodoItem {
    <#
    .SYNOPSIS
        Find all comments in the code base that have the 'TODO' keyword
    .DESCRIPTION
        Show a list of all "TODO comments" in the code base starting at the directory specified in Path
    .EXAMPLE
        Find-TodoItem $BuildRoot
    #>

    [OutputType('Stitch.SourceItem.Todo')]
    [CmdletBinding()]
    param(
        # Specifies a path to one or more locations.
        [Parameter(
        Position = 0,
        ValueFromPipeline,
        ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string[]]$Path
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $todoPattern = '^(\s*)(#)?\s*TODO(:)?\s+(.*)$'
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        #TODO: To refine this we could parse the file and use the comment tokens to give to Select-String
        $results = Get-ChildItem $Path -Recurse | Select-String -Pattern $todoPattern -CaseSensitive -AllMatches

         foreach ($result in $results) {
            [PSCustomObject]@{
                PSTypeName = 'Stitch.SourceItem.Todo'
                Text = $result.Matches[0].Groups[4].Value
                Position = (-join ($result.Path, ':', $result.LineNumber))
                File = (Get-Item $result.Path)
                Line = $result.LineNumber
            } | Write-Output
        }
 #>
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\SourceInfo\Find-TodoItem.ps1 47
#region source\stitch\public\SourceInfo\Get-ModuleItem.ps1 2
function Get-ModuleItem {
    <#
    .SYNOPSIS
        Retrieve the modules in the given path
    .DESCRIPTION
        Get-ModuleItem returns an object representing the information about the modules in the directory given in
        Path. It returns information from the manifest such as version number, etc. as well as SourceItemInfo
        objects for all of the source items found in it's subdirectories
    .EXAMPLE
        Get-ModuleItem .\source
    .LINK
        Get-SourceItem
    #>

    [OutputType('Stitch.ModuleItemInfo')]
    [CmdletBinding()]
    param(
        # Specifies a path to one or more locations containing Module Source
        [Parameter(
            Mandatory,
            Position = 0,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [ValidateNotNullOrEmpty()]
        [string[]]$Path,

        # Optionally return a hashtable instead of an object
        [Parameter(
        )]
        [switch]$AsHashTable
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        foreach ($p in $Path) {
            Write-Debug " Looking for module source in '$p'"
            try {
                $pathItem = Get-Item $p -ErrorAction Stop
                if (-not($pathItem.PSIsContainer)) {
                    Write-Verbose "$p is not a Directory, skipping"
                    continue
                }
                foreach ($modulePath in ($pathItem | Get-ChildItem -Directory)) {
                    $info = @{}
                    [ModuleFlag]$flags = [ModuleFlag]::None

                    $name = $modulePath.Name
                    Write-Debug " Module name is $name"
                    $info['Name'] = $name
                    $info['ModuleName'] = $name

                    $manifestFile = (Join-Path $modulePath "$name.psd1")
                    if (Test-Path $manifestFile) {
                        $manifestObject = Import-Psd $manifestFile
                        if (($manifestObject.Keys -contains 'PrivateData') -and
                            ($manifestObject.Keys -contains 'GUID')) {

                                [ModuleFlag]$flags = [ModuleFlag]::HasManifest
                                Write-Debug " Found $name.psd1 testing Manifest"
                                $info['ManifestFile'] = "$name.psd1"
                            }
                    }

                    $sourceInfo = Get-SourceItem $modulePath.Parent | Where-Object Module -like $name
                    if ($null -ne $sourceInfo) {
                        [ModuleFlag]$flags += [ModuleFlag]::HasModule
                    }

                    if ($flags.hasFlag([ModuleFlag]::HasManifest)) {
                        Write-Verbose "Manifest found in $($modulePath.BaseName)"
                        foreach ($key in $manifestObject.Keys) {
                            if ($key -notlike 'PrivateData') {
                                $info[$key] = $manifestObject[$key]
                            }
                        }
                        foreach ($key in $manifestObject.PrivateData.PSData.Keys) {
                            $info[$key] = $manifestObject.PrivateData.PSData[$key]
                        }
                    }
                    if ($flags.hasFlag([ModuleFlag]::HasModule)) {
                        $info['SourceDirectories'] = $sourceInfo |
                            Where-Object { @('function', 'class', 'enum') -contains $_.Type } |
                                Select-Object -ExpandProperty Directory | Sort-Object -Unique
                        $info['SourceInfo'] = $sourceInfo
                        if ($info.Keys -notcontains 'RootModule') {
                            $info['ModuleFile'] = "$name.psm1"
                        } else {
                            $info['ModuleFile'] = $info.RootModule
                        }
                        Write-Verbose "Module source found in $($modulePath.BaseName)"
                    }
                    if ($AsHashTable) {
                        $info | Write-Output
                    } else {
                        $info['PSTypeName'] = 'Stitch.ModuleItemInfo'
                        [PSCustomObject]$info | Write-Output
                    }
                }
            } catch {
                $PSCmdlet.ThrowTerminatingError($_)
            }
        }
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\SourceInfo\Get-ModuleItem.ps1 110
#region source\stitch\public\SourceInfo\Get-SourceItem.ps1 2
function Get-SourceItem {
    <#
    #>

    [CmdletBinding()]
    param(
        # Specifies a path to one or more locations.
        [Parameter(
            Position = 0,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string[]]$Path,

        # Path to the source type map
        [Parameter(
        )]
        [string]$TypeMap
    )

    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }

    process {
        if (-not($PSBoundParameters.ContainsKey('Path'))) {
            Write-Debug "No path specified. Using default source folder"
            $Path = (Join-Path (Resolve-ProjectRoot) 'source')
            Write-Debug " $Path"
        }
        foreach ($p in $Path) {
            try {
                $item = Get-Item $p -ErrorAction Stop
                if ($item.PSIsContainer) {
                    Get-ChildItem $item.FullName -Recurse:$Recurse |
                    Get-sourceItemInfo -Root $item.FullName | Write-Output
                    continue
                } else {
                    if ($item.Extension -eq '.ps1') {
                        $item | Get-SourceItemInfo | Write-Output
                    }
                    continue

                }
            } catch {
                Write-Warning "$p is not a valid path`n$_"
            }
        }
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\SourceInfo\Get-SourceItem.ps1 54
#region source\stitch\public\SourceInfo\Get-SourceTypeMap.ps1 2
function Get-SourceTypeMap {
    <#
    .SYNOPSIS
        Retrieve the table that maps source items to the appropriate Visibility and Type
    .LINK
        Get-SourceItemInfo
    #>

    [CmdletBinding()]
    param(
        # Specifies a path to the source type map file.
        [Parameter(
        Position = 0,
        ValueFromPipeline,
        ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string]$Path

    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $moduleMapName = 'script:_stitchSourceTypeMap'
    }
    process {
        <#
         ! If we already loaded the map, then return that one
         TODO: I need a better way to manage the items that depend on the variables set during Invoke-Build when not in Invoke-Build
         #>

        $map = (Get-Variable -Name $moduleMapName -ValueOnly -ErrorAction SilentlyContinue)
        if ($null -ne $map) {
            Write-Debug "found map in $moduleMapName"
            $map | Write-Output
        } else {
            Write-Debug   "Source type map not set. Creating now."
            if ($PSBoundParameters.ContainsKey('Path')) {
                New-SourceTypeMap -PassThru -Path $Path | Write-Output
            } else {
                New-SourceTypeMap -PassThru | Write-Output
            }
        }
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\SourceInfo\Get-SourceTypeMap.ps1 46
#region source\stitch\public\SourceInfo\Get-TestItem.ps1 1
function Get-TestItem {
    [CmdletBinding()]
    param(
        # Specifies a path to one or more locations.
        [Parameter(
            Position = 0,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string[]]$Path
    )

    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }

    process {
        if (-not($PSBoundParameters.ContainsKey('Path'))) {
            Write-Debug "No path specified. Looking for `$Tests"
            $testsVariable = $PSCmdlet.GetVariableValue('Tests')
            if ($null -ne $testsVariable) {
                Write-Debug " - found `$Tests: $testsVariable"
            } else {
                Write-Debug 'Checking for default tests folder'
                $possiblePath = (Join-Path (Resolve-ProjectRoot) 'tests')
                if ($null -ne $possiblePath) {
                    if (Test-Path $possiblePath) {
                        $Path = $possiblePath
                    }
                }
            }
            if ($null -eq $Path) {
                throw 'Could not resolve a Path to tests'
            } else {
                Write-Debug "Path is $Path"
            }
        }

        foreach ($p in $Path) {
            try {
                $item = Get-Item $p -ErrorAction Stop
            } catch {
                Write-Warning "$p is not a valid path`n$_"
                continue
            }
            if ($item.PSIsContainer) {
                try {
                    Get-ChildItem $item.FullName -Recurse:$Recurse -File |
                        Get-TestItemInfo -Root $item.FullName | Write-Output
                }
                catch {
                    Write-Warning "$_"
                }
                continue
            } else {
                if ($item.Extension -eq '.ps1') {
                    try {
                        $item | Get-TesItemtInfo | Write-Output
                    }
                    catch {
                        Write-Warning "$_"
                    }
                    continue
                }
                continue
            }
        }
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\SourceInfo\Get-TestItem.ps1 73
#region source\stitch\public\SourceInfo\New-FunctionItem.ps1 2
function New-FunctionItem {
    <#
    .SYNOPSIS
        Create a new function source item in the given module's source folder with the give visibility
    .EXAMPLE
        $module | New-FunctionItem Get-FooItem public
    .EXAMPLE
        New-FunctionItem Get-FooItem Foo public
    #>

    [CmdletBinding()]
    param(
        # The name of the Function to create
        [Parameter(
            Mandatory,
            Position = 0
        )]
        [string]$Name,

        # The name of the module to create the function for
        [Parameter(
            Mandatory,
            Position = 1,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [Alias('ModuleName')]
        [string]$Module,

        # Visibility of the function ('public' for exported commands, 'private' for internal commands)
        # defaults to 'public'
        [Parameter(
            Position = 2
        )]
        [ValidateSet('public', 'private')]
        [string]$Visibility = 'public',

        # Code to be added to the begin block of the function
        [Parameter(
        )]
        [string]$Begin,

        # Code to be added to the process block of the function
        [Parameter(
        )]
        [string]$Process,

        # Code to be added to the End block of the function
        [Parameter(
        )]
        [string]$End,

        # Optionally provide a component folder
        [Parameter(
        )]
        [string]$Component,

        # Overwrite an existing file
        [Parameter(
        )]
        [switch]$Force,

        # Return the path to the generated file
        [Parameter(
        )]
        [switch]$PassThru
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $projectPaths = Get-ProjectPath
        if ($null -ne $projectPaths) {
            if (-not ([string]::IsNullorEmpty($projectPaths.Source))) {
                if ($PSBoundParameters.ContainsKey('Module')) {
                    $modulePath = (Join-Path $projectPaths.Source $Module)
                }

                $filePath = (Join-Path -Path $modulePath -ChildPath $Visibility)

                if ($PSBoundParameters.ContainsKey('Component')) {
                    $filePath = (Join-Path $filePath $Component)
                    if (-not(Confirm-Path $filePath -ItemType Directory)) {
                        throw "Could not create source directory $filePath"
                    }
                }
                Write-Debug " - filePath is $filePath"

                $options = @{
                    Type     = 'function'
                    Name     = $Name
                    Destination = $filePath
                    Data     = @{ 'Name' = $Name }
                    Force    = $Force
                    PassThru = $PassThru
                }

                if ($PSBoundParameters.ContainsKey('Begin')) {
                    $options.Data['Begin'] = $Begin
                }
                if ($PSBoundParameters.ContainsKey('Process')) {
                    $options.Data['Process'] = $Process
                }
                if ($PSBoundParameters.ContainsKey('End')) {
                    $options.Data['End'] = $End
                }
                try {
                    New-SourceItem @options
                } catch {
                    $PSCmdlet.ThrowTerminatingError($_)
                }
            } else {
                throw 'Could not resolve Source directory'
            }
        } else {
            throw 'Could not get project path information'
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\SourceInfo\New-FunctionItem.ps1 124
#region source\stitch\public\SourceInfo\New-SourceComponent.ps1 2
function New-SourceComponent {
    <#
    .SYNOPSIS
        Add a new Component folder to the module's source
    #>

    [CmdletBinding()]
    param(
        # The name of the component to add
        [Parameter(
            Position = 0
        )]
        [string]$Name,

        # The name of the module to add the component to
        [Parameter(
            Position = 1,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName
        )]
        [string]$Module,

        # Only add the component to the public functions
        [Parameter(
            ParameterSetName = 'public'
            )]
            [switch]$PublicOnly,

            # Only add the component to the private functions
            [Parameter(
            ParameterSetName = 'private'
        )]
        [switch]$PrivateOnly
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $possibleSourceFolder = $PSCmdlet.GetVariableValue('Source')
        if ($null -eq $possibleSourceFolder) {
            $projectSourcePath = Get-ProjectPath | Select-Object -ExpandProperty Source
            Write-Debug "Project path value for Source: $projectSourcePath"
        } else {
            Write-Debug "Source path set from `$Source variable: $Source"
            $projectSourcePath = $possibleSourceFolder
        }
        $moduleDirectory = (Join-Path $projectSourcePath $Module)
        Write-Debug "Module directory is $moduleDirectory"
        if ($null -ne $moduleDirectory) {
            if (-not ($PublicOnly)) {
                $privateDirectory = (Join-Path $moduleDirectory 'private')
                if (Test-Path $privateDirectory) {
                    $null = (Join-Path $privateDirectory $Name) | Confirm-Path -ItemType Directory
                } else {
                    throw "Could not find $privateDirectory"
                }
            }
            if (-not ($PrivateOnly)) {
                $publicDirectory = (Join-Path $moduleDirectory 'public')
                if (Test-Path $publicDirectory) {
                    $null = (Join-Path $publicDirectory $Name) | Confirm-Path -ItemType Directory
                } else {
                    throw "Could not find $publicDirectory"
                }
            }
        } else {
            throw "Module source not found : $moduleDirectory"
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\SourceInfo\New-SourceComponent.ps1 75
#region source\stitch\public\SourceInfo\New-SourceItem.ps1 2
function New-SourceItem {
    <#
    .SYNOPSIS
        Create a new source item using templates
    .DESCRIPTION
        `New-SourceItem
    #>

    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact = 'Low'
    )]
    param(
        # The type of file to create
        [Parameter(
            Position = 0
        )]
        [string]$Type,

        # The file name
        [Parameter(
            Position = 1
        )]
        [string]$Name,

        # The data to pass into the template binding
        [Parameter(
            Position = 2
        )]
        [hashtable]$Data,

        # The directory to place the new file in
        [Parameter()]
        [string]$Destination,

        # Overwrite an existing file
        [Parameter(
        )]
        [switch]$Force,

        # Return the path to the generated file
        [Parameter(
        )]
        [switch]$PassThru
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"

    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"

        $template = Get-StitchTemplate -Type 'new' -Name $Type


        if ($null -ne $template) {
            if ($PSBoundParameters.ContainsKey('Name')) {
                $template.Name = $Name
            }
            if (-not ([string]::IsNullorEmpty($template.Extension))) {
                $template.Name = ( -join ($template.Name, $template.Extension))
            }

            if ($PSBoundParameters.ContainsKey('Destination')) {
                $template.Destination = $Destination
            }

            if ($PSBoundParameters.ContainsKey('Data')) {
                Write-Debug "Processing template Data"
                if (-not ([string]::IsNullorEmpty($template.Data))) {
                    Write-Debug " - Updating Data"
                    $template.Data = ($template.Data | Update-Object $Data)
                } else {
                    Write-Debug " - Setting Data"
                    $template.Data = $Data
                }
            }
            Write-Debug "Invoking template"
            $template | Invoke-StitchTemplate -Force:$Force -PassThru:$PassThru
        } else {
            throw "Could not find a 'new' template for type: $Type"
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }


}
#endregion source\stitch\public\SourceInfo\New-SourceItem.ps1 90
#region source\stitch\public\SourceInfo\New-SourceTypeMap.ps1 2
function New-SourceTypeMap {
    <#
    .SYNOPSIS
        Create a new mapping table of source items to their Visibility and Type
    .DESCRIPTION
        The source type map is used by `Get-SourceItemInfo` to set the Visibility and Type attribute on items found
        in the module source directories.
        `Visibility` and `Type` is used to determine if the item should be included in the module manifest `Export`
        keys (for example, if `Type` is `function` and `Visibility` is `public`, the item is listed in
        `FunctionsToExport`, etc.)
 
        `New-SourceTypeMap` looks in the following locations for data to populate the map:
        - The file sourcetypes.config.psd1 in the profile path, the build config path, or this module
        - The `SourceTypeMap` variable
        - The -TypeMap parameter
        Each, if found will update the existing type map
    #>

    [CmdletBinding()]
    param(
        # Specifies a path to a SourceTypeMap file
        [Parameter(
        Position = 0,
        ValueFromPipeline,
        ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string]$Path,

        # Hash table of mappings to create the new map from
        [Parameter(
        )]
        [hashtable]$TypeMap,

        # Send the object out to the pipeline in addition to setting the script scope variable
        [Parameter(
        )]
        [switch]$PassThru
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        # The name of the Module variable to store the map in once created
        $moduleMapName = 'script:_stitchSourceTypeMap'
        # The variable to look for in the current environment for a map
        $defaultMapVar = 'SourceTypeMap'
        # Used internally to the function to create the map
        $internalMap = @{}

        #-------------------------------------------------------------------------------
        #region file path
        if ($PSBoundParameters.ContainsKey('Path')) {
            if (Test-Path $Path) {
                if ((Get-Item $Path).PSIsContainer) {
                    $defaultMapFile = (Join-Path $Path 'sourcetypes.config.psd1')
                } else {
                    $defaultMapFile = $Path
                }
            }
        } elseif ($null -ne $BuildConfigPath) {
            #! always prefer the Profile's configuration
            $defaultMapFile = (Join-Path $BuildConfigPath 'sourcetypes.config.psd1')
        } elseif ($null -ne $BuildConfigRoot) {
            #! Then the build config directory (.build ?)
            $defaultMapFile = (Join-Path $BuildConfigRoot 'sourcetypes.config.psd1')
        } else {
            #! If those aren't found fall back to the modules internal config
            $defaultMapFile = (join-Path (Get-Item $MyInvocation.MyCommand.Module.Path).Directory 'sourcetypes.config.psd1')
        }
        #endregion file path
        #-------------------------------------------------------------------------------
    }
    process {
        # Load the file
        Write-Debug "Looking for source type map file '$defaultMapFile'"
        if (Test-Path $defaultMapFile) {
            Write-Debug "Updating source type map using '$defaultMapFile'"
            $null = $internalMap = $internalMap | Update-Object (Import-Psd $defaultMapFile)
        }

        # Load the variable
        Write-Debug "Looking for source type map variable '$defaultMapVar'"
        $map = (Get-Variable -Name $defaultMapVar -ValueOnly -ErrorAction SilentlyContinue)
        if ($null -ne $map) {
            Write-Debug "Updating source type map using `$$defaultMapVar"
            $null = $internalMap = $internalMap | Update-Object $map
        }

        # Load the Parameter
        Write-Debug "Looking for source type map Parameter 'TypeMap'"
        if ($PSBoundParameters.ContainsKey('TypeMap')) {
            Write-Debug "Updating source type map using -TypeMap parameter"
            $null = $internalMap = $internalMap | Update-Object $TypeMap
        }

        Write-Debug "Type map created. Updating `$SourceTypeMap table"
        Set-Variable -Name $moduleMapName -Value $internalMap -Scope 'Script'
        if ($PassThru) {
            $internalMap | Write-Output
        }
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\SourceInfo\New-SourceTypeMap.ps1 104
#region source\stitch\public\SourceInfo\New-TestItem.ps1 1
function New-TestItem {
    <#
    .SYNOPSIS
        Create a test item from a source item using the test template
    #>

    [CmdletBinding()]
    param(
        # The SourceItemInfo object to create the test from
        [Parameter(
            ValueFromPipeline
        )]
        [PSTypeName('Stitch.SourceItemInfo')]
        [Object[]]$SourceItem,

        # Overwrite an existing file
        [Parameter(
        )]
        [switch]$Force,

        # Return the path to the generated file
        [Parameter(
        )]
        [switch]$PassThru
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $projectPaths = Get-ProjectPath
        if ($null -ne $projectPaths) {
            if (-not ([string]::IsNullorEmpty($projectPaths.Source))) {
                $relativePath = [System.IO.Path]::GetRelativePath(($projectPaths.Source), $SourceItem.Path)
                Write-Debug "Relative Source path is $relativePath"
                $filePath = $relativePath -replace [regex]::Escape($SourceItem.FileName) , ''
                Write-Debug " - filePath is $filePath"
                $testName = "$filePath$([System.IO.Path]::DirectorySeparatorChar)$($SourceItem.BaseName).Tests.ps1"

                Write-Debug "Setting template Name to $testName"
                $options = @{
                    Type     = 'test'
                    Name     = $testName
                    Data     = @{ s = $SourceItem }
                    Force    = $Force
                    PassThru = $PassThru
                }
                try {
                    New-SourceItem @options
                } catch {
                    $PSCmdlet.ThrowTerminatingError($_)
                }
            } else {
                throw 'Could not resolve Source directory'
            }
        } else {
            throw 'Could not get project path information'
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\SourceInfo\New-TestItem.ps1 63
#region source\stitch\public\SourceInfo\Rename-SourceItem.ps1 2
function Rename-SourceItem {
    <#
    .SYNOPSIS
        Rename the file and the function, enum or class in the file
    #>

    [CmdletBinding()]
    param(
        # Specifies a path to one or more locations.
        [Parameter(
        Position = 1,
        ValueFromPipeline,
        ValueFromPipelineByPropertyName
        )]
        [Alias('PSPath')]
        [string[]]$Path,

        # The New name of the function
        [Parameter(
        )]
        [string]$NewName,

        # Return the new file object
        [Parameter(
        )]
        [switch]$PassThru
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $predicates = @{
            function = {
                param($ast)
                $ast -is [System.Management.Automation.Language.FunctionDefinitionAst]
            }
            class = {
                param($ast)
                (($ast -is [System.Management.Automation.Language.TypeDefinitionAst]) -and
                ($ast.Type -like 'Class'))
            }
            enum = {
                param($ast)
                (($ast -is [System.Management.Automation.Language.TypeDefinitionAst]) -and
                ($ast.Type -like 'Enum'))
            }
        }

    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        :file foreach ($file in $Path) {
            if (Test-Path $file) {
                $fileItem = Get-Item $file
                try {
                    $ast = [Parser]::ParseFile($fileItem.FullName, [ref]$null, [ref]$null)
                }
                catch {
                    throw "Could not parse source item $($fileItem.FullName)`n$_"
                }
                # try to find the type of SourceInfo this is
                $typeWasFound = $false
                :type foreach ($type in $predicates.GetEnumerator()) {
                    $innerAst = $ast.Find($type.Value, $false)
                    if ($null -ne $innerAst) {
                        $typeWasFound = $true
                        $oldName = $innerAst.Name
                        Write-Verbose "Found $($type.Name)"
                        break type
                    }
                }
                #! replace all occurances of the old name in the file
                $newExtent = $ast.Extent.Text -replace [regex]::Escape($oldName), $NewName
                try {
                    $newExtent | Set-Content -Path $fileItem.FullName
                    Write-Debug "Updating content in $($fileItem.Name)"
                }
                catch {
                    throw "Could not write content to $($fileItem.FullName)`n$_"
                }

                $baseDirectory = $fileItem | Split-Path -Parent
                $originalExtension = $fileItem.Extension # pretty sure this will always be .ps1, but ...
                $NewPath = (Join-Path $baseDirectory "$NewName$originalExtension")
                try {
                    Move-Item $fileItem.FullName -Destination $NewPath
                    Write-Debug "Renaming file to $NewPath"
                }
                catch {
                    throw "Could not rename $($fileItem.Name)"
                }
                if ($PassThru) {
                    Get-Item $NewPath
                }
            }
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\SourceInfo\Rename-SourceItem.ps1 100
#region source\stitch\public\Template\Get-StitchTemplate.ps1 2
function Get-StitchTemplate {
    [CmdletBinding()]
    param(
        # The type of template to retrieve
        [Parameter(
        )]
        [string]$Type,

        # The name of the template to retrieve
        [Parameter(
        )]
        [string]$Name
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $templatePath = (Join-Path (Get-ModulePath) 'templates')
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $templateTypes = @{}
        Get-ChildItem $templatePath -Directory | ForEach-Object {
            Write-Debug "Found template file '$($_.Name)' Adding as $"
            $templateTypes.Add($_.BaseName, $_.FullName)
        }
        foreach ($templateType in $templateTypes.GetEnumerator()) {
            $templates = Get-ChildItem $templateType.Value -Filter '*.eps1' -File
            foreach ($template in $templates) {
                $templateObject = [PSCustomObject]@{
                    PSTypeName  = 'Stitch.TemplateInfo'
                    Type        = $templateType.Name
                    Source      = $template.FullName
                    Destination = ''
                    Name        = $template.BaseName -replace '_', '.'
                    Description = ''
                    Data        = @{}
                }

                $metaData = Get-StitchTemplateMetadata
                if ($null -ne $metaData) {
                    $null = $templateObject | Update-Object -UpdateObject $metaData
                }

                #-------------------------------------------------------------------------------
                #region Set Target

                #! Making this a ScriptProperty means that when Destination or Name are updated
                #! this value will be updated to reflect
                if ([string]::IsNullorEmpty($templateObject.Target)) {
                    $templateObject | Add-Member ScriptProperty -Name Target -Value {
                        if ([string]::IsNullorEmpty($this.Destination)) {
                            $this.Destination = (Get-Location)
                        }
                        (Join-Path ($ExecutionContext.InvokeCommand.ExpandString($this.Destination)) $this.Name)
                    }
                }
                #endregion Set Name
                #-------------------------------------------------------------------------------


                #-------------------------------------------------------------------------------
                #region Set destination

                if ([string]::IsNullorEmpty($templateObject.Target)) {
                    #TODO: I don't think this is right. We should not be using 'path' for anything
                    if ($null -ne $templateObject.path) {
                        $templateObject.Destination = "$($templateObject.path)/$($templateObject.Target)"
                    }
                }

                #endregion Set destination
                #-------------------------------------------------------------------------------

                #-------------------------------------------------------------------------------
                #region Binding data
                if (-not ([string]::IsNullorEmpty($templateObject.bind))) {
                    $pathOptions = @{
                        Path = (Split-Path $template.FullName -Parent)
                        ChildPath = ($ExecutionContext.InvokeCommand.ExpandString($templateObject.bind))
                    }
                    $possibleDataFile = (Join-Path @pathOptions)
                    Write-Debug "Template has a bind parameter $possibleDataFile"
                    if (Test-Path $possibleDataFile) {
                        try {
                            $templateData = Import-Psd $possibleDataFile -Unsafe
                            $templateObject.Data = $templateObject.Data | Update-Object $templateData
                        }
                        catch {
                            throw "An error occurred updating $($templateObject.Name) template data`n$_"
                        }
                    }
                }
                #endregion Binding data
                #-------------------------------------------------------------------------------


                #-------------------------------------------------------------------------------
                #region Set display properties
                $defaultDisplaySet = 'Type', 'Name', 'Destination'
                $defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet', [string[]]$defaultDisplaySet)
                $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet)
                $templateObject | Add-Member MemberSet PSStandardMembers $PSStandardMembers

                #endregion Set display properties
                #-------------------------------------------------------------------------------

                #TODO: There is probably a better way to do this
                # if no parameters are set
                if ((-not ($PSBoundParameters.ContainsKey('Type'))) -and
                    (-not ($PSBoundParameters.ContainsKey('Name')))) {
                        $templateObject | Write-Output
                # if both are set and they match the object
                } elseif (($PSBoundParameters.ContainsKey('Type')) -and
                          ($PSBoundParameters.ContainsKey('Name'))) {
                    if (($templateObject.Type -like $Type) -and
                        ($templateObject.Name -like $Name)) {
                            $templateObject | Write-Output
                    }
                # if Type is set and it matches the object
                } elseif ($PSBoundParameters.ContainsKey('Type')) {
                    if ($templateObject.Type -like $Type) {
                        $templateObject | Write-Output
                    }
                # if Name is set and it matches the object
                } elseif ($PSBoundParameters.ContainsKey('Name')) {
                    if ($templateObject.Name -like $Name) {
                        $templateObject | Write-Output
                    }
                }
            }
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\Template\Get-StitchTemplate.ps1 137
#region source\stitch\public\VSCode\Get-CurrentEditorFile.ps1 1
function Get-CurrentEditorFile {
    [CmdletBinding()]
    param(
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        if ($null -ne $psEditor) {
            $psEditor.GetEditorContext().CurrentFile
        }
        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\VSCode\Get-CurrentEditorFile.ps1 18
#region source\stitch\public\VSCode\Get-VSCodeSetting.ps1 1
function Get-VSCodeSetting {
    [CmdletBinding()]
    param(
        # The name of the setting to return
        [Parameter(
            Position = 0
        )]
        [string]$Name,

        # Treat the Name as a regular expression
        [Parameter(
        )]
        [switch]$Regex
    )
    begin {
        Write-Debug "`n$('-' * 80)`n-- Begin $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        $settingsFile = "$env:APPDATA\Code\User\settings.json"
    }
    process {
        Write-Debug "`n$('-' * 80)`n-- Process start $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
        if (Test-Path $settingsFile) {
            Write-Debug "Loading the settings file"
            $settings = Get-Content $settingsFile | ConvertFrom-Json -Depth 16 -AsHashtable
        }

        if ($PSBoundParameters.ContainsKey('Name')) {
            if ($Regex) {
                Write-Debug "Looking for settings that match $Name"
                $matchedKeys = $settings.Keys | Where-Object { $_ -match $Name }
            } else {
                Write-Debug "Looking for settings that are like $Name"
                $matchedKeys = $settings.Keys | Where-Object { $_ -like $Name }
            }
            if ($matchedKeys.Count -gt 0) {
                Write-Debug "Found $($matchedKeys.Count) settings"
                $settingsSubSet = @{}
                foreach ($matchedKey in $matchedKeys) {
                    $settingsSubSet[$matchedKey] = $settings[$matchedKey]
                }
                Write-Debug "Creating settings subset"
                $settings = $settingsSubSet
            }
        }

        $settings['PSTypeName'] = 'VSCode.SettingsInfo'
        [PSCustomObject]$settings | Write-Output

        Write-Debug "`n$('-' * 80)`n-- Process end $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
    end {
        Write-Debug "`n$('-' * 80)`n-- End $($MyInvocation.MyCommand.Name)`n$('-' * 80)"
    }
}
#endregion source\stitch\public\VSCode\Get-VSCodeSetting.ps1 53
#endregion Public functions
#===============================================================================

#===============================================================================
#region suffix

Set-Alias -Name Import-BuildScript -Value "$PSScriptRoot\Import-BuildScript.ps1"
Set-Alias -Name Import-TaskFile -Value "$PSScriptRoot\Import-TaskFile.ps1"
#endregion suffix
#===============================================================================