Alt3.Docusaurus.Powershell.psm1

#Region 'PREFIX' 0
Set-StrictMode -Version Latest
$PSDefaultParameterValues['*:ErrorAction'] = 'Stop' # full stop on first error
#EndRegion 'PREFIX'
#Region '.\Private\CreateOrCleanFolder.ps1' 0
function CreateOrCleanFolder() {
    <#
        .SYNOPSIS
            Helper function to create a folder OR remove it's contents if it already exists.
    #>

    param(
        [Parameter(Mandatory = $True)][string]$Path
    )

    # create the folder if it does not exist
    if (-not(Test-Path -Path $Path)) {
        Write-Verbose "=> creating folder $($Path)"
        New-Item -Path $Path -ItemType Directory -Force

        return
    }

    # otherwise remove it's contents
    Write-Verbose "=> cleaning folder $($Path)"
    Remove-Item -Path (Join-Path -Path $Path -ChildPath *.*)
}
#EndRegion '.\Private\CreateOrCleanFolder.ps1' 21
#Region '.\Private\GetCustomEditUrl.ps1' 0
function GetCustomEditUrl() {
    <#
        .SYNOPSIS
            Returns the `custom_edit_url` for the given .md file.
 
        .DESCRIPTION
            Generates a URL pointing to the Powershell source file that was used to generate the markdown file.
 
        .NOTES
            - URLs for non-monolithic modules point to a .ps1 file with same name as the markdown file
            - URLs for monolithic modules will always point to a .psm1 with same name as passed module
    #>

    param(
        [Parameter(Mandatory = $True)][string]$Module,
        [Parameter(Mandatory = $True)][System.IO.FileSystemInfo]$MarkdownFile,
        [Parameter(Mandatory = $False)][string]$EditUrl,
        [switch]$Monolithic
    )

    # return "false" so Docusaurus will not render the `Edit this page` button
    if (-not($EditUrl)) {
        return
    }

    # point to the function source file for non-monlithic modules
    if (-not($Monolithic)) {
        $command = [System.IO.Path]::GetFileNameWithoutExtension($MarkdownFile)

        return $EditUrl + '/' + $command + ".ps1"
    }

    # point to the module source file for monolithic modules
    if (Test-Path $Module) {
        $Module = [System.IO.Path]::GetFileNameWithoutExtension($Module)
    }

    return $EditUrl + '/' + $Module + ".psm1"
}
#EndRegion '.\Private\GetCustomEditUrl.ps1' 38
#Region '.\Private\InitializeTempFolder.ps1' 0
function InitializeTempFolder() {
    <#
        .SYNOPSIS
            Creates the temp folder and the `debug.info` file.
 
        .DESCRIPTION
            The temp folder is where all work is done before the enriched mdx files are copied
            to the docusaurus sidebar folder. We use this approach to support future debugging
            as it will be near impossible to reason about bugs without looking at the initial
            PlatyPS files, knowing which Powershell version was used etc.
 
            We might even instruct users to send us the files when reporting issues (which
            would require re-generating using the `-KeepTempFiles` switch).
 
        .NOTES
            Ideally we would also log used module versions for Alt3, PlatyPS and Pester.
    #>

    param(
        [Parameter(Mandatory = $True)][string]$Path
    )

    Write-Verbose "Initializing temp folder:"
    CreateOrCleanFolder -Path $Path

    # prepare the debug info
    Write-Verbose "=> creating debug file"
    $debugInfo = [ordered]@{
        PSVersionTable = $PSVersionTable
    } | ConvertTo-Json

    # create the debug file
    $debugFile = Join-Path -Path $Path -ChildPath "debug.json"
    $fileEncoding = New-Object System.Text.UTF8Encoding $False
    [System.IO.File]::WriteAllLines($debugFile, $debugInfo, $fileEncoding)
}
#EndRegion '.\Private\InitializeTempFolder.ps1' 35
#Region '.\Private\NewMarkdownExample.ps1' 0
function NewMarkdownExample() {
    <#
        .SYNOPSIS
            Generates a new markdown example block.
    #>

    param(
        [Parameter(Mandatory = $True)][string]$Header,
        [Parameter(Mandatory = $True)][string]$Code,
        [Parameter(Mandatory = $False)][string]$Description = $null
    )

    $example = "$Header`n"
    $example += '```powershell' + "`n"
    $example += $Code
    $example += '```' + "`n"

    if ([string]::IsNullOrEmpty($Description)) {
        $example += "`n"
    } else {
        $example += "`n$Description`n"
    }

    return $example
}
#EndRegion '.\Private\NewMarkdownExample.ps1' 24
#Region '.\Private\NewSidebarIncludeFile.ps1' 0
function NewSidebarIncludeFile() {
    <#
        .SYNOPSIS
            Generates a `.js` file holding an array with all .mdx 'ids` to be imported in Docusaurus `sidebar.js`.
 
        .LINK
            https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-powershell-1.0/ff730948(v=technet.10)
    #>

    param(
        [Parameter(Mandatory = $True)][string]$OutputFolder,
        [Parameter(Mandatory = $True)][string]$Sidebar,
        [Parameter(Mandatory = $True)][Object]$MarkdownFiles
    )

    Write-Verbose "Generating docusaurus.sidebar.js"

    # generate a list of Powershell commands by stripping .md from the generated PlatyPs files
    [array]$commands = $MarkdownFiles | Select-Object @{ Name = "PowershellCommand"; Expression={ "'$Sidebar/" + [System.IO.Path]::GetFileNameWithoutExtension($_) + "'" } } | Select-Object  -Expand PowershellCommand

    # generate content using Here-String block
$content = @"
/**
 * Import this file in your Docusaurus ``sidebars.js`` file.
 *
 * Auto-generated by Alt3.Powershell.Docusaurus.
 *
 * Copyright (c) 2019-present, ALT3 B.V.
 *
 * Licensed under the MIT license.
 */
 
module.exports = [
    $($commands -Join ",`n ")
];
"@


    # generate file path, convert relative output folder to absolute if needed
    if (-Not([System.IO.Path]::IsPathRooted($OutputFolder))) {
        $outputFolder = Join-Path "$(Get-Location)" -ChildPath $OutputFolder
    }

    $filePath = Join-Path -Path $outputFolder -ChildPath "docusaurus.sidebar.js"

    # create the file
    $fileEncoding = New-Object System.Text.UTF8Encoding $False
    [System.IO.File]::WriteAllLines($filePath, $content, $fileEncoding)
}
#EndRegion '.\Private\NewSidebarIncludeFile.ps1' 47
#Region '.\Private\RemoveFile.ps1' 0
function RemoveFile() {
    <#
        .SYNOPSIS
            Helper function to remove a file if it exists.
    #>

    param(
        [Parameter(Mandatory = $True)][string]$Path
    )

    Write-Verbose "=> removing $Path"

    if (Test-Path -Path $Path) {
        Remove-Item -Path $Path -Force
    }
}
#EndRegion '.\Private\RemoveFile.ps1' 15
#Region '.\Private\RemoveMarkdownHeaderOne.ps1' 0
function RemoveMarkdownHeaderOne() {
    <#
        .SYNOPSIS
        Removes the H1 element from the markdown content because Docusaurus already generates H1 using the `title` front matter.
    #>

    param(
        [Parameter(Mandatory = $True)][System.IO.FileSystemInfo]$MarkdownFile
    )

    $content = (Get-Content -Path $MarkdownFile.FullName -Raw).TrimEnd()

    $regex = '\n#{1}\s.+'

    $newContent = [regex]::replace($content, $regex, '')

    # replace file (UTF-8 without BOM)
    $fileEncoding = New-Object System.Text.UTF8Encoding $False
    [System.IO.File]::WriteAllLines($markdownFile.FullName, $newContent, $fileEncoding)
}
#EndRegion '.\Private\RemoveMarkdownHeaderOne.ps1' 19
#Region '.\Private\ReplaceMarkdownExamples.ps1' 0
function ReplaceMarkdownExamples() {
    <#
        .SYNOPSIS
            Replace PlatyPS generated code block examples.
 
        .DESCRIPTION
            Replaces custom fenced code blocks and placeholder examples, otherwise uses PlatyPS generated defaults.
 
            See link below for a detailed description of the determination process.
 
        .LINK
            https://github.com/alt3/Docusaurus.Powershell/issues/14#issuecomment-568552556
    #>

    param(
        [Parameter(Mandatory = $True)][System.IO.FileSystemInfo]$MarkdownFile,
        [switch]$NoPlaceHolderExamples
    )

    $content = (Get-Content -Path $MarkdownFile.FullName -Raw).TrimEnd()
    [string]$newExamples = ""

    # ---------------------------------------------------------------------
    # extract all EXAMPLE nodes
    # https://regex101.com/r/y4UxP8/2
    # ---------------------------------------------------------------------
    $regexExtractExamples = [regex]::new('### (EXAMPLE|Example) [0-9][\s\S]*?(?=\n.*?#|$)')
    $examples = $regexExtractExamples.Matches($content)

    if ($examples.Count -eq 0) {
        Write-Warning "Unable to find any EXAMPLE nodes. Please check your Get-Help definitions before filing an issue!"
    }

    # process each EXAMPLE node
    $examples | ForEach-Object {
        $example = $_

        # ---------------------------------------------------------------------
        # do not modify if it's a PlatyPS placeholder example
        # https://regex101.com/r/WOQL0l/4
        # ---------------------------------------------------------------------
        $regexPlatyPlaceholderExample = [regex]::new('{{ Add example code here }}')
        if ($example -match $regexPlatyPlaceholderExample) {

            if ($NoPlaceHolderExamples) {
                Write-Verbose "=> Example 1: PlatyPS Placeholder (dropping)"
                return
            }

            Write-Verbose "=> Example 1: PlatyPS Placeholder (keeping)"
            $newExamples += "$example`n"
            return
        }

        # ---------------------------------------------------------------------
        # Powershell 6: re-construct Code Fenced example
        # - https://regex101.com/r/lHdZHM/6 => without a description
        # - https://regex101.com/r/CGjQco/3 => with a description
        # ---------------------------------------------------------------------
        $regexPowershell6TripleCodeFence = [regex]::new('(### EXAMPLE ([0-9|[0-9]+))\n(```\n(```|```ps|```posh|```powershell)\n```\n)\n([\s\S]*?)\\`\\`\\`(\n\n|\n)([\s\S]*|\n)')

        if ($example -match $regexPowershell6TripleCodeFence) {
            $header = $matches[1]
            $code = $matches[5]
            $description = $matches[7]

            Write-Verbose "=> $($header): Triple Code Fence (Powershell 6 and lower)"

            $newExample = NewMarkdownExample -Header $header -Code $code -Description $description
            $newExamples += $newExample
            return
        }

        # ---------------------------------------------------------------------
        # Powershell 7: re-construct PlatyPS Paired Code Fences example
        # - https://regex101.com/r/FRA139/1 => without a description
        # - https://regex101.com/r/YIIwUs/5 => with a description
        # ---------------------------------------------------------------------
        $regexPowershell7PairedCodeFences = [regex]::new('(### EXAMPLE ([0-9]|[0-9]+))\n(```\n(```|```ps|```posh|```powershell)\n)([\s\S]*?)```\n```(\n\n|\n)([\s\S]*|\n)')

        if ($example -match $regexPowershell7PairedCodeFences) {
            $header = $matches[1]
            $code = $matches[5]
            $description = $matches[7]

            Write-Verbose "=> $($header): Paired Code Fences (Powershell 7)"

            $newExample = NewMarkdownExample -Header $header -Code $code -Description $description
            $newExamples += $newExample
            return
        }

        # ---------------------------------------------------------------------
        # Powershell 7: re-construct non-adjacent Code Fenced example
        # - https://regex101.com/r/kLr98l/3 => without a description
        # - https://regex101.com/r/eJH4cQ/6 => with a complex description
        # ---------------------------------------------------------------------
        $regexPowershell7NonAdjacentCodeBlock = [regex]::new('(### EXAMPLE ([0-9]|[0-9]+))\n(```\n(```|```ps|```posh|```powershell)\n)([\s\S]*?)\\`\\`\\`(\n\n([\s\S]*)|\n)')

        if ($example -match $regexPowershell7NonAdjacentCodeBlock) {
            $header = $matches[1]
            $code = $matches[5] -replace ('```' + "`n"), ''
            $description = $matches[7]

            Write-Verbose "=> $($header): Non-Adjacent Code Block (Powershell 7)"

            $newExample = NewMarkdownExample -Header $header -Code $code -Description $description
            $newExamples += $newExample
            return
        }

        # ---------------------------------------------------------------------
        # no matches so we simply use the unaltered PlatyPS generated example
        # - https://regex101.com/r/rllmTj/1 => without a decription
        # - https://regex101.com/r/kTH75U/1 => with a description
        # ---------------------------------------------------------------------
        $regexPlatyPsDefaults = [regex]::new('(### EXAMPLE ([0-9]|[0-9]+))\n```\n([\s\S]*)```\n([\s\S]*)')

        if ($example -match $regexPlatyPsDefaults) {
            $header = $matches[1]
            $code = $matches[5] -replace ('```' + "`n"), ''
            $description = $matches[7]

            Write-Verbose "=> $($header): PlatyPS Default (all Powershell versions)"

            $newExamples += "$example`n"
            return
        }

        # we should never reach this point
        Write-Warning "Unsupported code block detected, please file an issue containing the error message below at https://github.com/alt3/Docusaurus.Powershell/issues"
        Write-Warning $example
    }

    # replace EXAMPLES section in content with updated examples
    # https://regex101.com/r/8OEW0w/1/
    $regex = '## EXAMPLES\n[\s\S]+## PARAMETERS'
    $replacement = "## EXAMPLES`n`n$($newExamples)## PARAMETERS"
    $content = [regex]::replace($content, $regex, $replacement)

    # replace file (UTF-8 without BOM)
    $fileEncoding = New-Object System.Text.UTF8Encoding $False
    [System.IO.File]::WriteAllLines($markdownFile.FullName, $content, $fileEncoding)
}
#EndRegion '.\Private\ReplaceMarkdownExamples.ps1' 143
#Region '.\Private\SetMarkdownCodeBlockMoniker.ps1' 0
function SetMarkdownCodeBlockMoniker() {
    <#
        .SYNOPSIS
        Add `powershell` moniker to generated code blocks for correct syntax highlighting.
 
        .NOTES
        1. unfortunately we need to do this because PlatyPS does not add the language (design choice)
        2. @todo change regex so it will match on \n as well (now only works on CRLF files)
    #>

    param(
        [Parameter(Mandatory = $True)][System.IO.FileSystemInfo]$MarkdownFile
    )

    $content = (Get-Content -Path $MarkdownFile.FullName -Raw).TrimEnd()

    # this regex replaces all opening code fences without a language moniker with "```powershell"
    # https://regex101.com/r/AYzALd/1
    $regex = '(```)\n((?:(?!```)[\s\S])+)(```)\n'

    $content = [regex]::replace($content, $regex, '```powershell' + "`n" + '$2```' + "`n")

    # replace file (UTF-8 without BOM)
    $fileEncoding = New-Object System.Text.UTF8Encoding $False
    [System.IO.File]::WriteAllLines($markdownFile.FullName, $content, $fileEncoding)
}
#EndRegion '.\Private\SetMarkdownCodeBlockMoniker.ps1' 25
#Region '.\Private\SetMarkdownFrontMatter.ps1' 0
function SetMarkdownFrontMatter() {
    <#
        .SYNOPSIS
            Replaces PlatyPS generated front matter with Docusaurus compatible front matter.
 
        .LINK
            https://www.apharmony.com/software-sagacity/2014/08/multi-line-regular-expression-replace-in-powershell/
    #>

    param(
        [Parameter(Mandatory = $True)][System.IO.FileSystemInfo]$MarkdownFile,
        [Parameter(Mandatory = $False)][string]$CustomEditUrl,
        [Parameter(Mandatory = $False)][string]$MetaDescription,
        [Parameter(Mandatory = $False)][array]$MetaKeywords,
        [switch]$HideTitle,
        [switch]$HideTableOfContents
    )

    $powershellCommandName = [System.IO.Path]::GetFileNameWithoutExtension($markdownFile.Name)

    # prepare front matter
    $newFrontMatter = [System.Collections.ArrayList]::new()
    $newFrontMatter.Add("---") | Out-Null
    $newFrontMatter.Add("id: $($powershellCommandName)") | Out-Null
    $newFrontMatter.Add("title: $($powershellCommandName)") | Out-Null

    if ($MetaDescription) {
        $description = [regex]::replace($MetaDescription, '%1', $powershellCommandName)
        $newFrontMatter.Add("description: $($description)") | Out-Null
    }

    if ($MetaKeywords) {
        $newFrontMatter.Add("keywords:") | Out-Null
        $MetaKeywords | ForEach-Object {
            $newFrontMatter.Add(" - $($_)") | Out-Null
        }
    }
    $newFrontMatter.Add("hide_title: $(if ($HideTitle) {"true"} else {"false"})") | Out-Null
    $newFrontMatter.Add("hide_table_of_contents: $(if ($HideTableOfContents) {"true"} else {"false"})") | Out-Null

    if ($EditUrl) {
        $newFrontMatter.Add("custom_edit_url: $($EditUrl)") | Out-Null
    }

    $newFrontMatter.Add("---") | Out-Null

    # replace front matter
    $content = (Get-Content -Path $MarkdownFile.FullName -Raw).TrimEnd()

    $regex = "(?sm)^(---)(.+)^(---).$\n"

    $newContent = $content -replace $regex, ($newFrontMatter | Out-String)

    # replace file (UTF-8 without BOM)
    $fileEncoding = New-Object System.Text.UTF8Encoding $False
    [System.IO.File]::WriteAllLines($markdownFile.FullName, $newContent, $fileEncoding)
}
#EndRegion '.\Private\SetMarkdownFrontMatter.ps1' 56
#Region '.\Private\SetMarkdownLineEndings.ps1' 0
function SetMarkdownLineEndings() {
    <#
        .SYNOPSIS
            Replaces all CRLF line endings with LF so we can consitently use/expect `n when regexing etc.
    #>

    param(
        [Parameter(Mandatory = $True)][System.IO.FileSystemInfo]$MarkdownFile
    )

    $content = (Get-Content -Path $MarkdownFile.FullName -Raw).TrimEnd()

    $content = $content -replace "`r`n", "`n"

    # replace file (UTF-8 without BOM)
    $fileEncoding = New-Object System.Text.UTF8Encoding $False
    [System.IO.File]::WriteAllLines($markdownFile.FullName, $content, $fileEncoding)
}
#EndRegion '.\Private\SetMarkdownLineEndings.ps1' 17
#Region '.\Private\UpdateMarkdownBackticks.ps1' 0
function UpdateMarkdownBackticks() {
    <#
        .SYNOPSIS
        Replaces platyPS-produced "escaped backticks" with normal backticks so markdown gets rendered as expected.
    #>

    param(
        [Parameter(Mandatory = $True)][System.IO.FileSystemInfo]$MarkdownFile
    )

    $content = (Get-Content -Path $MarkdownFile.FullName -Raw).TrimEnd()

    $regex = '\\`'

    $newContent = [regex]::replace($content, $regex, '`')

    # replace file (UTF-8 without BOM)
    $fileEncoding = New-Object System.Text.UTF8Encoding $False
    [System.IO.File]::WriteAllLines($markdownFile.FullName, $newContent, $fileEncoding)
}
#EndRegion '.\Private\UpdateMarkdownBackticks.ps1' 19
#Region '.\Public\New-DocusaurusHelp.ps1' 0
function New-DocusaurusHelp() {
    <#
        .SYNOPSIS
            Generates Get-Help documentation in Docusaurus compatible `.mdx` format.
 
        .DESCRIPTION
            The `New-DocusaurusHelp` cmdlet generates Get-Help documentation in Docusaurus
            compatible format by creating an `.mdx` file for each command exported by
            the module, enriched with command-specific front matter variables.
 
            Also creates a `sidebar.js` file for simplified integration into the Docusaurus sidebar menu.
 
        .OUTPUTS
            System.Object
 
        .EXAMPLE
            New-DocusaurusHelp -Module Alt3.Docusaurus.Powershell
 
            This example uses default settings to generate a Get-Help page for each command exported by
            the Alt3.Docusaurus.Powershell module.
 
        .EXAMPLE
            ```
            $parameters = @{
                Module = "Alt3.Docusaurus.Powershell"
                DocsFolder = "D:\my-project\docs"
                Sidebar = "commands"
                Exclude = @(
                    "Get-SomeCommand"
                )
                MetaDescription = 'Help page for the Powershell command "%1"'
                MetaKeywords = @(
                    "Powershell"
                    "Documentation"
                )
            }
 
            New-DocusaurusHelp @parameters
            ```
 
            This example uses splatting to override default settings.
 
            See the list of Parameters below for all available overrides.
 
        .PARAMETER Module
            Specifies the module this cmdlet will generate Docusaurus documentation for.
 
            You may specify a module name, a `.psd1` file or a `.psm1` file.
 
        .PARAMETER DocsFolder
            Specifies the absolute or relative **path** to the Docusaurus `docs` folder.
 
            Optional, defaults to `docusaurus/docs`, case sensitive.
 
        .PARAMETER Sidebar
            Specifies the **name** of the docs subfolder in which the `.mdx` files will be created.
 
            Optional, defaults to `commands`, case sensitive.
 
        .PARAMETER Exclude
            Optional array with command names to exclude.
 
        .PARAMETER MetaDescription
            Optional string that will be inserted into Docusaurus front matter to be used as html meta tag 'description'.
 
            If placeholder `%1` is detected in the string, it will be replaced by the command name.
 
        .PARAMETER MetaKeywords
            Optional array of keywords inserted into Docusaurus front matter to be used as html meta tag `keywords`.
 
        .PARAMETER EditUrl
            Specifies the URL prefixed to all Docusaurus `custom_edit_url` front matter variables.
 
            Optional, defaults to `null`.
 
        .PARAMETER HideTitle
            Sets the Docusaurus front matter variable `hide_title`.
 
            Optional, defaults to `false`.
 
        .PARAMETER HideTableOfContents
            Sets the Docusaurus front matter variable `hide_table_of_contents`.
 
            Optional, defaults to `false`.
 
        .PARAMETER NoPlaceholderExamples
            By default, Docusaurus will generate a placeholder example if your Get-Help
            definition does not contain any `EXAMPLE` nodes.
 
            You can use this switch to disable that behavior which will result in an empty `EXAMPLES` section.
 
        .PARAMETER Monolithic
            Use this optional parameter if the Powershell module source is monolithic.
 
            Will point all `custom_edit_url` front matter variables to the `.psm1` file.
 
        .NOTES
            Please note that Docusaurus v2 is an early and alpha version, just like this module.
 
        .LINK
            https://v2.docusaurus.io/
 
        .LINK
            https://github.com/PowerShell/platyPS
    #>

    [cmdletbinding()]
    param(
        [Parameter(Mandatory = $True)][string]$Module,
        [Parameter(Mandatory = $False)][string]$DocsFolder = "docusaurus/docs",
        [Parameter(Mandatory = $False)][string]$Sidebar = "commands",
        [Parameter(Mandatory = $False)][array]$Exclude = @(),
        [Parameter(Mandatory = $False)][string]$EditUrl,
        [Parameter(Mandatory = $False)][string]$MetaDescription,
        [Parameter(Mandatory = $False)][array]$MetaKeywords = @(),
        [switch]$HideTitle,
        [switch]$HideTableOfContents,
        [switch]$NoPlaceHolderExamples,
        [switch]$Monolithic
    )

    # make sure the passed module is valid
    if (Test-Path($Module)) {
        Import-Module $Module -Force -Global
        $Module = [System.IO.Path]::GetFileNameWithoutExtension($Module)
    }

    if (-Not(Get-Module -Name $Module)) {
        $Module = $Module
        throw "New-DocusaurusHelp: Specified module '$Module' is not loaded"
    }

    $moduleName = [io.path]::GetFileName($module)

    # markdown for the module will be copied into the sidebar subfolder
    Write-Verbose "Initializing sidebar folder:"
    $sidebarFolder = Join-Path -Path $DocsFolder -ChildPath $Sidebar
    CreateOrCleanFolder -Path $sidebarFolder

    # create tempfolder used for generating the PlatyPS files and creating the mdx files
    $tempFolder = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath "Alt3.Docusaurus.Powershell" | Join-Path -ChildPath $moduleName
    InitializeTempFolder -Path $tempFolder

    # generate PlatyPs markdown files
    Write-Verbose "Generating PlatyPS files."
    New-MarkdownHelp -Module $Module -OutputFolder $tempFolder -Force | Out-Null

    # remove excluded files
    Write-Verbose "Removing excluded files:"
    $Exclude | ForEach-Object {
        RemoveFile -Path (Join-Path -Path $tempFolder -ChildPath "$($_).md")
    }

    # rename PlatyPS files and create an `.mdx` copy we will transform
    Write-Verbose "Cloning PlatyPS files."
    Get-ChildItem -Path $tempFolder -Filter *.md | ForEach-Object {
        $platyPsFile = $_.FullName -replace '.md', '.PlatyPS.md'
        $mdxFile = $_.FullName -replace '.md', '.mdx'
        Move-Item -Path $_.FullName -Destination $platyPsFile
        Copy-Item  -Path $platyPsFile -Destination $mdxFile
    }

    # update all remaining mdx files to make them Docusaurus compatible
    Write-Verbose "Updating mdx files."
    $mdxFiles = Get-ChildItem -Path $tempFolder -Filter *.mdx

    ForEach ($mdxFile in $mdxFiles) {
        Write-Verbose "Processing $($mdxFile.Name):"

        SetMarkdownLineEndings -MarkdownFile $mdxFile

        $customEditUrl = GetCustomEditUrl -Module $Module -MarkdownFile $mdxFile -EditUrl $EditUrl -Monolithic:$Monolithic

        $frontMatterArgs = @{
            MarkdownFile = $mdxFile
            MetaDescription = $metaDescription
            CustomEditUrl = $customEditUrl
            MetaKeywords = $metaKeywords
            HideTitle = $HideTitle
            HideTableOfContents = $HideTableOfContents
        }
        SetMarkdownFrontMatter @frontmatterArgs

        RemoveMarkdownHeaderOne -MarkdownFile $mdxFile
        ReplaceMarkdownExamples -MarkdownFile $mdxFile -NoPlaceholderExamples:$NoPlaceholderExamples
        SetMarkdownCodeBlockMoniker -MarkdownFile $mdxFile
        UpdateMarkdownBackticks -MarkdownFile $mdxFile
    }

    # copy updated mdx files to the target folder
    Write-Verbose "Copying mdx files to sidebar folder."
    Get-ChildItem -Path $tempFolder -Filter *.mdx | ForEach-Object {
        # $mdxFile = $_.FullName -replace '.md', '.mdx'
        Copy-Item  -Path $_.FullName -Destination (Join-Path -Path $sidebarFolder -ChildPath ($_.Name))
    }

    # generate the `.js` file used for the docusaurus sidebar
    NewSidebarIncludeFile -MarkdownFiles $mdxFiles -OutputFolder $sidebarFolder -Sidebar $Sidebar

    # zip temp files in case we need them
    Compress-Archive -Path (Join-Path -Path $tempFolder -ChildPath *.*) -DestinationPath (Join-Path $tempFolder -ChildPath "$moduleName.zip")

    # output Get-ChildItem so end-user can post-process generated files as they see fit
    Get-ChildItem -Path $sidebarFolder
}
#EndRegion '.\Public\New-DocusaurusHelp.ps1' 204