Skip to content

Commit

Permalink
Merge pull request #17 from alt3/multiline-examples
Browse files Browse the repository at this point in the history
Now cross-version consistent (Powershell 5, 6, 7) with support for PS7 multi-line code examples
  • Loading branch information
bravo-kernel authored Dec 29, 2019
2 parents 7fa984d + 9701b3f commit da48ced
Show file tree
Hide file tree
Showing 38 changed files with 1,267 additions and 215 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[![Azure DevOps coverage](https://img.shields.io/azure-devops/coverage/alt3bv/Docusaurus.Powershell/2?style=flat-square)](https://dev.azure.com/alt3bv/Docusaurus.Powershell/_build)
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v1.4%20adopted-ff69b4.svg?style=flat-square)](code-of-conduct.md)

Showcase websites for Powershell Modules.
Documentation websites for Powershell Modules.

## Live Demo

Expand Down
21 changes: 21 additions & 0 deletions Source/Private/CreateOrCleanFolder.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
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 *.*)
}
11 changes: 0 additions & 11 deletions Source/Private/GetMdxFilePath.ps1

This file was deleted.

35 changes: 35 additions & 0 deletions Source/Private/InitializeTempFolder.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
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)
}
24 changes: 24 additions & 0 deletions Source/Private/NewMarkdownExample.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
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
}
9 changes: 4 additions & 5 deletions Source/Private/NewSidebarIncludeFile.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ function NewSidebarIncludeFile() {
[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

Expand All @@ -32,17 +34,14 @@ module.exports = [
];
"@

# generate file path, convert relative outputfolder to absolute if needed
# 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"
$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)

# add created file to output
Get-Item $filePath
}
15 changes: 15 additions & 0 deletions Source/Private/RemoveFile.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
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
}
}
2 changes: 1 addition & 1 deletion Source/Private/RemoveMarkdownHeaderOne.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ function RemoveMarkdownHeaderOne() {

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

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

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

Expand Down
143 changes: 143 additions & 0 deletions Source/Private/ReplaceMarkdownExamples.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
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)
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
function UpdateMarkdownCodeBlocks() {
function SetMarkdownCodeBlockMoniker() {
<#
.SYNOPSIS
Add `powershell` syntax highlighting to generated code blocks.
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)
Expand All @@ -13,12 +13,13 @@ function UpdateMarkdownCodeBlocks() {

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

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

$newContent = [regex]::replace($content, $regex, '```powershell$2```')
$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, $newContent, $fileEncoding)
[System.IO.File]::WriteAllLines($markdownFile.FullName, $content, $fileEncoding)
}
17 changes: 17 additions & 0 deletions Source/Private/SetMarkdownLineEndings.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
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)
}
Loading

0 comments on commit da48ced

Please sign in to comment.