PowerShell / platyPS

Write PowerShell External Help in Markdown
MIT License
762 stars 148 forks source link

Support for data from XMLdoc for binary modules #618

Open svrooij opened 7 months ago

svrooij commented 7 months ago

Summary of the new feature / enhancement

As a developer of a binary powershell module I would like to document in inside the C# files, as I'm used to for creating C# libraries.

Proposed technical implementation details (optional)

I suggest supporting XML docs above the PsCmdLet as follows:

/// <summary>
/// <para type="synopsis">Synopsis here</para>
/// <para type="description">Long description here</para>
/// <para type="link" uri="https://wintuner.app/docs/related/content-prep-tool">Documentation</para> 
/// </summary>
/// <example>
/// <para type="description">Sample description here</para>
/// <code>Sample code here</code>
/// </example>

And I wrote a powershell script that parses the generated C# xml documentation file and updates the markdown files accordingly. This powershell replaces the placeholders with data from the xml docs. If the data is already updated nothing will happen. Off-course it would be better to integrate this right into the normal c# code that does the markdown generation. Something like -GenerateUsingXmlDocs <docsLocation>.

  1. Execute New-MarkdownHelp ... first, to create the docs files.
  2. Put this script in the root of your project.
  3. Execute this script to update the markdown files, and to create the external help file in the root of your project.
  4. Mark the external help file as None and Copy Always in the properties in Visual Studio.
# change accordingly
$xmlDocsPath = ".\bin\Release\net7.0\ProjectName.xml"
$docsFolder = "docs"

# Start Process to build the project
$buildOutput = & dotnet build -c Release -v quiet

# Check if the build succeeded by looking for the string "Build succeeded."
if ($buildOutput -match "Build succeeded.") {
    Write-Output "Project build succeeded"
}
else {
    Write-Output "Build failed"
    Write-Output $buildOutput
    exit
}

Write-Output "Generating docs from XML file $xmlDocsPath"

# Load the XML documentation file
[xml]$xmlDocs = Get-Content $xmlDocsPath

$assemblyName = $xmlDocs.doc.assembly.name
Write-Debug "Updating docs for Assembly: $assemblyName"

$members = $xmlDocs.doc.members.member

# Iterate over all <member> objects
foreach ($member in $members) {
    # member looks like this:
    # <member name="T:Your.Namespace.ClassNameForPsCmdLet">
    #   <summary>
    #   <para type="synopsis">synopsis here</para>
    #   <para type="description">PsCmdLet description here</para>
    #   <para type="link" uri="https://wintuner.app/docs/related/content-prep-tool">Documentation</para> 
    #   </summary>
    #   <example>
    #   <para type="description">Sample description here</para>
    #   <code>Sample Code here</code>
    #   </example>
    # </member>
    # Extract the name of the member
    $name = $member.Attributes[0].'#text'

    if ($name.startsWith("T:" + $assemblyName)) {
            $name = $name.substring(2 + $assemblyName.length + 1)
        # name NewIntuneWinPackage
        Write-Output "Try to update markdown file for: $name"

        # Extract the synopsis
        $synopsis = $member.summary.'para' | Where-Object { $_.type -eq 'synopsis' } | Select-Object -ExpandProperty '#text'
        Write-Debug "Synopsis: $synopsis"

        # Extract the description
        $description = $member.summary.'para' | Where-Object { $_.type -eq 'description' } | Select-Object -ExpandProperty '#text'
        Write-Debug "Description: $description"

        # Extract the example
        $exampleDescription = $member.example.'para'| Where-Object { $_.type -eq 'description' } | Select-Object -ExpandProperty '#text'
        $exampleCode = $member.example.'code'

        Write-Debug "Example Description: $exampleDescription"
        Write-Debug "Example Code: $exampleCode"

        # Create the MD file name by putting a - only before the second capital letter, so NewIntuneWinPackage becomes New-IntuneWinPackage
        $index = $name.IndexOfAny([char[]]"ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray(), 1)
        $mdFile = $name.Substring(0, $index) + "-" + $name.Substring($index) + ".md"
        Write-Debug "MD Filename: $mdFile"

        # Check if the $mdFile exists in the docs folder
        $mdFilePath = Join-Path "${PSScriptRoot}\$docsFolder" $mdFile
        if (Test-Path $mdFilePath) {
            # load the existing file
            $mdFileContent = Get-Content $mdFilePath

            # Replace the synopsis placeholder '{{ Fill in the Synopsis }}' with the actual synopsis
            $mdFileContent = $mdFileContent.Replace('{{ Fill in the Synopsis }}', $synopsis)

            # Replace the description placeholder '{{ Fill in the Description }}' with the actual description
            $mdFileContent = $mdFileContent.Replace('{{ Fill in the Description }}', $description)

            # Replace the example description placeholder '{{ Add example description here }}' with the actual example description
            $mdFileContent = $mdFileContent.Replace('{{ Add example description here }}', $exampleDescription)

            # Replace the example code placeholder '{{ Add example code here }}' with the actual example code
            $mdFileContent = $mdFileContent.Replace('{{ Add example code here }}', $exampleCode)

            # Write the updated file back to disk
            $mdFileContent | Set-Content $mdFilePath
        }
        else {
            Write-Warning "File $mdFilePath does not exist"
        }
    }
}

Write-Output "Done updating markdown files with XML documentation"

New-ExternalHelp -Path "${PSScriptRoot}\$docsFolder" -OutputPath "${PSScriptRoot}" -Force
svrooij commented 4 days ago

@sdwheeler are you accepting a PR for this? And can you help me get started? I have no clue where to look.

sdwheeler commented 4 days ago

@svrooij We are not considering support for XML docs at this time. We may consider it in the future. This would require further investigation.

svrooij commented 3 days ago

I see the code in src\Markdown.MAML is not published as a nuget, can I just use a submodule to take that code and use it to write the xml file myself?

sdwheeler commented 3 days ago

No. That code is not supported and it is being replaced by a complete rewrite. You shouldn't take any dependencies on that code.

With the new version of PlatyPS that is being developed, you will import the cmdlet markdown files into a PowerShell object. You can then change property values in the object to update the documentation. So you could read documentation from the XMLdoc and inject it into the object. Then you write that object out as Markdown, Yaml, or MAML.