├── test └── Pester │ ├── assets │ ├── bad-commandhelp.md │ ├── ThreadJob.md │ ├── Bad.Metadata.Order.md │ ├── Microsoft.PowerShell.Host.md │ ├── Microsoft.PowerShell.Archive.md │ ├── Microsoft.PowerShell.Diagnostics.md │ ├── CBH_ASSET1.ps1 │ ├── Get-MPIOSetting.md │ ├── PSDiagnostics.md │ ├── New-EmptyCommand.md │ ├── Exit-PSHostProcess.md │ ├── PSReadLine.md │ ├── CimCmdlets.md │ ├── CimCmdlets2.md │ ├── Microsoft.WSMan.Management.md │ ├── Microsoft.PowerShell.Security.md │ ├── Compare-CommandHelp2.md │ ├── Out-Null.md │ ├── Compare-CommandHelp.md │ └── Microsoft.SystemCenter.ServiceManagementAutomation.md │ ├── CommonFunction.ps1 │ ├── Miscellaneous.Tests.ps1 │ ├── YamlFormatting.Tests.ps1 │ ├── InteractiveExperience.Tests.ps1 │ ├── MeasurePlatyPSMarkdown.Tests.ps1 │ ├── MarkdownYaml.Tests.ps1 │ ├── ImportYamlCommandHelp.Tests.ps1 │ ├── TestMarkdownCommandHelp.Tests.ps1 │ ├── DiagnosticMessage.Tests.ps1 │ ├── ImportMamlHelp.Tests.ps1 │ ├── Model.Tests.ps1 │ ├── CompareCommandHelp.Tests.ps1 │ ├── GetMarkdownMetadata.Tests.ps1 │ ├── ParagraphFormatting.Tests.ps1 │ ├── ExportMarkdownModuleFile.Tests.ps1 │ └── ExportYamlModuleFile.Tests.ps1 ├── global.json ├── src ├── AssemblyInfo.cs ├── Common │ ├── CommandHelpWriterSettings.cs │ ├── StringBuilderPool.cs │ ├── YamlLayoutSerializer.cs │ ├── OrderedSet.cs │ ├── TransformationAttribute.cs │ └── ParagraphFormatHelper.cs ├── YamlWriter │ ├── YamlWriterSettings.cs │ ├── QuotedNullStringEventEmitter.cs │ ├── YamlConstants.cs │ └── CommandHelpYamlWriterHelper.cs ├── MarkdownWriter │ ├── MarkdownWriterSettings.cs │ └── MarkdownConstants.cs ├── MamlWriter │ ├── CommandLine.cs │ ├── AlertSet.cs │ ├── PipelineInputType.cs │ ├── SyntaxItem.cs │ ├── ParameterValue.cs │ ├── CommandValue.cs │ ├── CommandDetails.cs │ ├── CommandExample.cs │ ├── HelpItems.cs │ ├── Constants.cs │ ├── RelatedLink.cs │ ├── MamlConstants.cs │ ├── DataType.cs │ └── Parameter.cs ├── Diagnostics │ ├── DiagnosticMessageSource.cs │ ├── DiagnosticSeverity.cs │ ├── Diagnostics.cs │ └── DiagnosticMessage.cs ├── MarkdownReader │ ├── MarkdownProbe.cs │ ├── MarkdownCommandHelpValidationResult.cs │ ├── ModuleCommandInfo.cs │ └── ModuleCommandGroup.cs ├── Transform │ ├── TransformModule.cs │ ├── TransformSettings.cs │ └── TransformCommand.cs ├── Microsoft.PowerShell.PlatyPS.csproj ├── Command │ ├── ConvertToCommandHelpCommand.cs │ ├── ImportModuleFileCommand.cs │ ├── Measure-PlatyPSMarkdown.cs │ ├── TestMarkdownCommandHelp.cs │ ├── ImportMamlCommand.cs │ ├── ImportMarkdownCommand.cs │ └── ImportYamlModuleFileCommand.cs ├── Model │ ├── Links.cs │ ├── Example.cs │ ├── ParameterSet.cs │ └── InputOutput.cs └── Microsoft.PowerShell.PlatyPS.psd1 ├── .vscode ├── settings.json └── tasks.json ├── .config ├── suppress.json └── tsaoptions.json ├── .github ├── CODEOWNERS └── ISSUE_TEMPLATE.md ├── nuget.config ├── docs ├── README.md └── archive │ ├── alias-prototype.md │ ├── platyps_2.0_about_schema.md │ └── Schema-Notes.md ├── LICENSE ├── tools └── releaseBuild │ └── template │ └── publish.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── .ci ├── test.yml └── ci.yml ├── platyPS.sln ├── ThirdPartyNotices.txt ├── README.md └── .gitignore /test/Pester/assets/bad-commandhelp.md: -------------------------------------------------------------------------------- 1 | echo # Bad Help 2 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "8.0.100", 4 | "rollForward": "latestMajor" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/Pester/CommonFunction.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | function normalizeEnds([string]$text) 5 | { 6 | $text -replace "`r`n?|`n", "`r`n" 7 | } 8 | -------------------------------------------------------------------------------- /src/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Runtime.CompilerServices; 5 | 6 | [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.PowerShell.PlatyPS.Tests")] 7 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out/**": true 5 | }, 6 | "dotnet.defaultSolution": "platyPS.sln", 7 | "sarif-viewer.connectToGithubCodeScanning": "off" 8 | } -------------------------------------------------------------------------------- /.config/suppress.json: -------------------------------------------------------------------------------- 1 | { 2 | "tool": "Credential Scanner", 3 | "suppressions": [ 4 | { 5 | "file": "\\test\\Pester\\assets\\Microsoft.PowerShell.Commands.Utility.dll-Help.xml", 6 | "_justification": "Documentation example." 7 | } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # https://help.github.com/articles/about-codeowners/ 2 | 3 | # These owners will be the default owners for everything in 4 | # the repo. Unless a later match takes precedence, 5 | # the following owners will be requested for 6 | # review when someone opens a pull request. 7 | * @adityapatwardhan @sdwheeler 8 | -------------------------------------------------------------------------------- /.config/tsaoptions.json: -------------------------------------------------------------------------------- 1 | { 2 | "instanceUrl": "https://msazure.visualstudio.com", 3 | "projectName": "One", 4 | "areaPath": "One\\MGMT\\Compute\\Powershell\\Powershell\\PowerShell Core\\Platyps", 5 | "codebaseName": "TFSMSAzure_platyPS", 6 | "notificationAliases": [ "jimtru@microsoft.com", "slee@microsoft.com" ], 7 | "tools": [ "CredScan", "PoliCheck", "BinSkim" ] 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/test/Markdown.MAML.Test/Markdown.MAML.Test.csproj" 11 | ], 12 | "problemMatcher": "$msCompile" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | Steps to reproduce 7 | ------------------ 8 | 9 | 10 | Expected behavior 11 | ----------------- 12 | 13 | 14 | Actual behavior 15 | --------------- 16 | 17 | 18 | Environment data 19 | ---------------- 20 | 21 | 22 | 23 | v0.5.0 24 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # PlatyPS design documentation 2 | 3 | This folder contains design documentation for the PlatyPS project. The documentation is intended for 4 | contributors to the PlatyPS project and provides information on the design decisions, architecture, 5 | and implementation details of the module. 6 | 7 | The `archive` folder contains older versions of the design documents for reference. These documents 8 | don't reflect the current state of the project and are provided for historical context. 9 | 10 | New design documents will be added to this folder. 11 | -------------------------------------------------------------------------------- /src/Common/CommandHelpWriterSettings.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Text; 5 | 6 | namespace Microsoft.PowerShell.PlatyPS 7 | { 8 | /// 9 | /// Settings for the command help writers. 10 | /// 11 | internal class WriterSettings 12 | { 13 | internal Encoding Encoding { get; set; } 14 | internal string DestinationPath { get; set; } 15 | 16 | public WriterSettings(Encoding encoding, string destinationPath) 17 | { 18 | Encoding = encoding; 19 | DestinationPath = destinationPath; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/YamlWriter/YamlWriterSettings.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Text; 5 | 6 | namespace Microsoft.PowerShell.PlatyPS.YamlWriter 7 | { 8 | /// 9 | /// Settings for the yaml writer. 10 | /// 11 | internal class YamlWriterSettings 12 | { 13 | internal Encoding Encoding { get; set; } 14 | internal string DestinationPath { get; set; } 15 | 16 | public YamlWriterSettings(Encoding encoding, string destinationPath) 17 | { 18 | Encoding = encoding; 19 | DestinationPath = destinationPath; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MarkdownWriter/MarkdownWriterSettings.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Text; 5 | 6 | namespace Microsoft.PowerShell.PlatyPS.MarkdownWriter 7 | { 8 | /// 9 | /// Settings for the markdown writer. 10 | /// 11 | internal class MarkdownWriterSettings 12 | { 13 | internal Encoding Encoding { get; set; } 14 | internal string DestinationPath { get; set; } 15 | 16 | public MarkdownWriterSettings(Encoding encoding, string destinationPath) 17 | { 18 | Encoding = encoding; 19 | DestinationPath = destinationPath; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MamlWriter/CommandLine.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Xml.Serialization; 5 | 6 | namespace Microsoft.PowerShell.PlatyPS.MAML 7 | { 8 | /// 9 | /// Represents a "command" element in a Powershell MAML help document. 10 | /// 11 | [XmlRoot("commandLine", Namespace = Constants.XmlNamespace.Command)] 12 | public class CommandLine 13 | { 14 | /// 15 | /// The command text. 16 | /// 17 | [XmlElement("commandText", Namespace = Constants.XmlNamespace.Command)] 18 | public string CommandText { get; set; } = string.Empty; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/Pester/assets/ThreadJob.md: -------------------------------------------------------------------------------- 1 | --- 2 | Download Help Link: https://aka.ms/powershell75-help 3 | Help Version: 7.5.0.0 4 | Locale: en-US 5 | Module Guid: 0e7b895d-2fec-43f7-8cae-11e8d16f6e40 6 | Module Name: ThreadJob 7 | ms.date: 07/09/2019 8 | title: ThreadJob Module 9 | --- 10 | 11 | # ThreadJob Module 12 | 13 | ## Description 14 | This module extends the existing PowerShell BackgroundJob to include a new thread based 15 | **ThreadJob** job. This is a lighter weight solution for running concurrent PowerShell scripts that 16 | works within the existing PowerShell job infrastructure. 17 | 18 | ## ThreadJob Cmdlets 19 | 20 | ### [Start-ThreadJob](Start-ThreadJob.md) 21 | Creates background jobs similar to the `Start-Job` cmdlet. 22 | -------------------------------------------------------------------------------- /src/Diagnostics/DiagnosticMessageSource.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | /// 5 | /// The diagnostic message source; Something that identifies the element to which diagnostic message pertains. 6 | /// 7 | public enum DiagnosticMessageSource 8 | { 9 | Metadata, 10 | Synopsis, 11 | Syntax, 12 | Alias, 13 | Description, 14 | Example, 15 | Parameter, 16 | Inputs, 17 | Outputs, 18 | Notes, 19 | Links, 20 | General, 21 | Merge, 22 | ModuleFileTitle, 23 | ModuleFileDescription, 24 | ModuleFileGroup, 25 | ModuleFileCommand, 26 | Identify 27 | } -------------------------------------------------------------------------------- /src/MarkdownReader/MarkdownProbe.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | 6 | namespace Microsoft.PowerShell.PlatyPS 7 | { 8 | /// 9 | /// A class used for probing markdown to determine its characteristics. 10 | /// 11 | public class MarkdownProbe 12 | { 13 | /// 14 | /// Identify the type of platyps file based on the path. 15 | /// 16 | /// The path to a markdown file. 17 | /// MarkdownProbeInfo 18 | public static MarkdownProbeInfo Identify(string path) 19 | { 20 | return new MarkdownProbeInfo(path); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/MamlWriter/AlertSet.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections.Generic; 5 | using System.Xml.Serialization; 6 | 7 | namespace Microsoft.PowerShell.PlatyPS.MAML 8 | { 9 | /// 10 | /// Represents a "command:syntaxItem" element in a Powershell MAML help document. 11 | /// 12 | public class AlertItem 13 | { 14 | /// 15 | /// The command name for this syntax. 16 | /// 17 | [XmlElement("para", Namespace = Constants.XmlNamespace.MAML, Order = 0)] 18 | public List Remark { get; set; } 19 | 20 | public AlertItem() 21 | { 22 | Remark = new(); 23 | } 24 | 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/Pester/assets/Bad.Metadata.Order.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Bad.Metadata.Order 3 | Module Guid: 56d66100-99a0-4ffc-a12d-eee9a6718aef 4 | Module Name: Microsoft.PowerShell.Host 5 | Help Version: 7.5.0.0 6 | Locale: en-US 7 | ms.date: 06/09/2017 8 | Download Help Link: https://aka.ms/powershell75-help 9 | PlatyPS version schema: 2024-05-01 10 | --- 11 | 12 | # Bad.Metadata.Order Module 13 | 14 | ## Description 15 | 16 | This section contains the help topics for the cmdlets that are installed with the PowerShell 17 | Microsoft.PowerShell.Host module. The Host module contains cmdlets that manage data from host 18 | programs. 19 | 20 | ## Bad.Metadata.Order Cmdlets 21 | 22 | ### [Start-Transcript](Start-Transcript.md) 23 | Creates a record of all or part of a PowerShell session to a text file. 24 | 25 | ### [Stop-Transcript](Stop-Transcript.md) 26 | Stops a transcript. 27 | 28 | -------------------------------------------------------------------------------- /test/Pester/assets/Microsoft.PowerShell.Host.md: -------------------------------------------------------------------------------- 1 | --- 2 | Download Help Link: https://aka.ms/powershell75-help 3 | Help Version: 7.5.0.0 4 | Locale: en-US 5 | Module Guid: 56d66100-99a0-4ffc-a12d-eee9a6718aef 6 | Module Name: Microsoft.PowerShell.Host 7 | ms.date: 06/09/2017 8 | schema: 2.0.0 9 | title: Microsoft.PowerShell.Host 10 | --- 11 | # Microsoft.PowerShell.Host Module 12 | 13 | ## Description 14 | 15 | This section contains the help topics for the cmdlets that are installed with the PowerShell 16 | Microsoft.PowerShell.Host module. The Host module contains cmdlets that manage data from host 17 | programs. 18 | 19 | ## Microsoft.PowerShell.Host Cmdlets 20 | 21 | ### [Start-Transcript](Start-Transcript.md) 22 | Creates a record of all or part of a PowerShell session to a text file. 23 | 24 | ### [Stop-Transcript](Stop-Transcript.md) 25 | Stops a transcript. 26 | 27 | -------------------------------------------------------------------------------- /src/Diagnostics/DiagnosticSeverity.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | 8 | namespace Microsoft.PowerShell.PlatyPS.Model 9 | { 10 | public enum DiagnosticSeverity 11 | { 12 | /// 13 | /// The diagnostic message represents an error. 14 | /// 15 | Error = 0, 16 | 17 | /// 18 | /// The diagnostic message represents a warning. 19 | /// 20 | Warning = 1, 21 | 22 | /// 23 | /// The diagnostic message represents an information message. 24 | /// 25 | Information = 2, 26 | 27 | /// 28 | /// The diagnostic message represents a hint. 29 | /// 30 | Hint = 3 31 | } 32 | } -------------------------------------------------------------------------------- /test/Pester/assets/Microsoft.PowerShell.Archive.md: -------------------------------------------------------------------------------- 1 | --- 2 | Download Help Link: https://aka.ms/powershell75-help 3 | Help Version: 7.5.0.0 4 | Locale: en-US 5 | Module Guid: eb74e8da-9ae2-482a-a648-e96550fb8733 6 | Module Name: Microsoft.PowerShell.Archive 7 | ms.date: 06/09/2017 8 | schema: 2.0.0 9 | title: Microsoft.PowerShell.Archive 10 | --- 11 | 12 | # Microsoft.PowerShell.Archive Module 13 | 14 | ## Description 15 | 16 | This section contains the help topics for the cmdlets that are installed with the PowerShell Microsoft.PowerShell.Archive module. The Archive module contains cmdlets that let you create and extract archive or ZIP files. 17 | 18 | ## Microsoft.PowerShell.Archive Cmdlets 19 | 20 | ### [Compress-Archive](Compress-Archive.md) 21 | Creates a compressed archive, or zipped file, from specified files and directories. 22 | 23 | ### [Expand-Archive](Expand-Archive.md) 24 | Extracts files from a specified archive (zipped) file. 25 | 26 | -------------------------------------------------------------------------------- /src/MamlWriter/PipelineInputType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Xml.Serialization; 3 | 4 | namespace Microsoft.PowerShell.PlatyPS.MAML 5 | { 6 | /// 7 | /// Supported Powershell pipeline input types for Cmdlet parameters. 8 | /// 9 | [Flags] 10 | public enum PipelineInputType 11 | { 12 | /// 13 | /// Parameter does not take its value from the pipeline. 14 | /// 15 | [XmlEnum("false")] 16 | None = 0, 17 | 18 | /// 19 | /// Parameter can take its value from the pipeline. 20 | /// 21 | [XmlEnum("true (ByValue)")] 22 | ByValue = 1, 23 | 24 | /// 25 | /// Parameter can take its value from a property of the same name on objects in the pipeline. 26 | /// 27 | [XmlEnum("true (ByPropertyName)")] 28 | ByPropertyName = 2 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/MamlWriter/SyntaxItem.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections.Generic; 5 | using System.Xml.Serialization; 6 | 7 | namespace Microsoft.PowerShell.PlatyPS.MAML 8 | { 9 | /// 10 | /// Represents a "command:syntaxItem" element in a Powershell MAML help document. 11 | /// 12 | public class SyntaxItem 13 | { 14 | /// 15 | /// The command name for this syntax. 16 | /// 17 | [XmlElement("name", Namespace = Constants.XmlNamespace.MAML, Order = 0)] 18 | public string CommandName { get; set; } = string.Empty; 19 | 20 | /// 21 | /// The command parameters associated with this syntax. 22 | /// 23 | [XmlElement("parameter", Namespace = Constants.XmlNamespace.Command, Order = 1)] 24 | public List Parameters = new List(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/MamlWriter/ParameterValue.cs: -------------------------------------------------------------------------------- 1 | using System.Xml.Serialization; 2 | 3 | namespace Microsoft.PowerShell.PlatyPS.MAML 4 | { 5 | /// 6 | /// Represents a "parameterValue" element in a Powershell MAML help document. 7 | /// 8 | [XmlRoot("parameterValue", Namespace = Constants.XmlNamespace.Command)] 9 | public class ParameterValue 10 | { 11 | /// 12 | /// The parameter data-type ("#text"). 13 | /// 14 | [XmlText] 15 | public string DataType { get; set; } = string.Empty; 16 | 17 | /// 18 | /// Is the parameter mandatory? 19 | /// 20 | [XmlAttribute("required")] 21 | public bool IsMandatory { get; set; } 22 | 23 | /// 24 | /// Is the parameter's data type variable-length? 25 | /// 26 | [XmlAttribute("variableLength")] 27 | public bool IsVariableLength { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/YamlWriter/QuotedNullStringEventEmitter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Runtime.Remoting.Metadata.W3cXsd2001; 5 | using YamlDotNet.Core; 6 | using YamlDotNet.Core.Events; 7 | using YamlDotNet.Serialization; 8 | using YamlDotNet.Serialization.EventEmitters; 9 | 10 | public class QuotedNullStringEventEmitter : ChainedEventEmitter 11 | { 12 | public QuotedNullStringEventEmitter(IEventEmitter nextEmitter) : base(nextEmitter) 13 | { 14 | } 15 | 16 | public override void Emit(ScalarEventInfo eventInfo, YamlDotNet.Core.IEmitter emitter) 17 | { 18 | if (eventInfo.Source.Type == typeof(string) && string.Equals(eventInfo.Source.Value?.ToString() , "NULL", System.StringComparison.OrdinalIgnoreCase)) 19 | { 20 | emitter.Emit(new Scalar(@"'NULL'")); 21 | } 22 | else 23 | { 24 | // For all other values, use the default emitter 25 | base.Emit(eventInfo, emitter); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/MarkdownReader/MarkdownCommandHelpValidationResult.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace Microsoft.PowerShell.PlatyPS 8 | { 9 | public class MarkdownCommandHelpValidationResult 10 | { 11 | public string Path { get; set; } 12 | public bool IsValid { get; set; } 13 | public List Messages { get; set; } 14 | public MarkdownCommandHelpValidationResult() 15 | { 16 | Path = string.Empty; 17 | IsValid = true; 18 | Messages = new List(); 19 | } 20 | 21 | public MarkdownCommandHelpValidationResult(string path, bool isValid, List messages) 22 | { 23 | Path = path; 24 | IsValid = isValid; 25 | Messages = messages; 26 | } 27 | 28 | public override string ToString() 29 | { 30 | return $"Path: {Path}, IsValid: {IsValid}"; 31 | } 32 | 33 | } 34 | } -------------------------------------------------------------------------------- /test/Pester/Miscellaneous.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | Describe "Miscellaneous tests" { 5 | Context "Alias preservation" { 6 | BeforeAll { 7 | $cmdHelpPath = [io.path]::Combine($PSScriptRoot, "assets", "Get-ChildItem.V2.md") 8 | $ch_md = Import-MarkdownCommandHelp $cmdHelpPath 9 | $fPath = $ch_md | Export-YamlCommandHelp -output $TESTDRIVE -Force 10 | $ch_yaml = Import-YamlCommandHelp $fPath 11 | } 12 | 13 | It "Should preserve the aliases" { 14 | $ch_md.Aliases | Should -Not -BeNullOrEmpty 15 | $ch_md.Aliases | Should -Be $ch_yaml.Aliases 16 | } 17 | 18 | It "Should have the correct diagnostic message from the markdown parse" { 19 | $message = $ch_md.Diagnostics.Messages.Where({$_.source -eq "alias" -and $_.Identifier -match "length"}) 20 | $message | Should -Not -BeNullOrEmpty 21 | $message.Identifier | Should -Match "118" 22 | } 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Transform/TransformModule.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections.ObjectModel; 5 | using System.Management.Automation; 6 | 7 | using Microsoft.PowerShell.PlatyPS.Model; 8 | 9 | namespace Microsoft.PowerShell.PlatyPS 10 | { 11 | internal class TransformModule : TransformBase 12 | { 13 | public TransformModule(TransformSettings settings) : base(settings) 14 | { 15 | } 16 | 17 | internal override Collection Transform(string[] moduleNames) 18 | { 19 | Collection cmdHelp = new(); 20 | 21 | foreach (var module in moduleNames) 22 | { 23 | Collection cmdletInfos = PowerShellAPI.GetCmdletInfoFromModule(module, Settings.Session); 24 | 25 | foreach (var cmdletInfo in cmdletInfos) 26 | { 27 | cmdHelp.Add(ConvertCmdletInfo(cmdletInfo)); 28 | } 29 | } 30 | 31 | return cmdHelp; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/Pester/assets/Microsoft.PowerShell.Diagnostics.md: -------------------------------------------------------------------------------- 1 | --- 2 | Download Help Link: https://aka.ms/powershell75-help 3 | Help Version: 7.5.0.0 4 | Locale: en-US 5 | Module Guid: ca046f10-ca64-4740-8ff9-2565dba61a4f 6 | Module Name: Microsoft.PowerShell.Diagnostics 7 | ms.date: 06/09/2017 8 | schema: 2.0.0 9 | title: Microsoft.PowerShell.Diagnostics 10 | --- 11 | # Microsoft.PowerShell.Diagnostics Module 12 | 13 | ## Description 14 | 15 | This section contains the help topics for the cmdlets that are installed with the PowerShell 16 | Microsoft.PowerShell.Diagnostics module, which contains cmdlets that manage data from event logs. 17 | 18 | This module is only available on the Windows platform. 19 | 20 | ## Microsoft.PowerShell.Diagnostics Cmdlets 21 | 22 | ### [Get-Counter](Get-Counter.md) 23 | Gets performance counter data from local and remote computers. 24 | 25 | ### [Get-WinEvent](Get-WinEvent.md) 26 | Gets events from event logs and event tracing log files on local and remote computers. 27 | 28 | ### [New-WinEvent](New-WinEvent.md) 29 | Creates a new Windows event for the specified event provider. 30 | 31 | -------------------------------------------------------------------------------- /src/MamlWriter/CommandValue.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections.Generic; 5 | using System.Xml.Serialization; 6 | 7 | namespace Microsoft.PowerShell.PlatyPS.MAML 8 | { 9 | /// 10 | /// Represents a "command:inputType" or "command:returnValue" element in a Powershell MAML help document. 11 | /// 12 | public class CommandValue 13 | { 14 | /// 15 | /// The value data-type. 16 | /// 17 | [XmlElement("type", Namespace = Constants.XmlNamespace.Dev, Order = 0)] 18 | public DataType DataType { get; set; } = new DataType(); 19 | 20 | /// 21 | /// The value's detailed description (one or more paragraphs; "maml:description/maml:para"). 22 | /// 23 | [XmlArray("description", Namespace = Constants.XmlNamespace.MAML, Order = 1)] 24 | [XmlArrayItem("para", Namespace = Constants.XmlNamespace.MAML)] 25 | public List Description { get; set; } = new List(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/Pester/YamlFormatting.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | Describe 'Yaml formatting tests' { 5 | It 'Inputs output and notes should have empty array when empty' { 6 | Import-MarkdownCommandHelp -Path 'assets/New-EmptyCommand.md' | Export-YamlCommandHelp -OutputFolder $TestDrive 7 | $yaml = Get-Content "$TestDrive/New-EmptyCommand.yml" -Raw 8 | 9 | $isWinPS = $PSVersionTable.PSVersion.Major -eq 5 10 | 11 | $inputPattern = if ($IsWindows -or $IsWinPS) { 'inputs:\r\n- name: System\.String\r\n description: ''' } else { 'inputs:\n- name: System\.String\n description: ''' } 12 | $outputPattern = if ($IsWindows -or $IsWinPS) { 'outputs:\r\n- name: System\.String\r\n description: ''' } else { 'outputs:\n- name: System\.String\n description: ''' } 13 | $notesPattern = "notes: ''" 14 | $linksPattern = "links: \[\]" 15 | 16 | $yaml | Should -Match $inputPattern 17 | $yaml | Should -Match $outputPattern 18 | $yaml | Should -Match $notesPattern 19 | $yaml | Should -Match $linksPattern 20 | } 21 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/Pester/assets/CBH_ASSET1.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Adds a file name extension to a supplied name. 4 | .DESCRIPTION 5 | Adds a file name extension to a supplied name. 6 | Takes any strings for the file name or extension. 7 | 8 | ```powershell 9 | PS> echo 'code block 1' 10 | ``` 11 | 12 | more explanation 13 | 14 | ```powershell 15 | PS> echo 'code block 2' 16 | ``` 17 | 18 | even more explanations 19 | 20 | .PARAMETER Second 21 | Second parameter help description 22 | .OUTPUTS 23 | System.String. Add-Extension returns a string with the extension or file name. 24 | .EXAMPLE 25 | PS C:\> Test-PlatyPSFunction "File" 26 | File.txt 27 | .EXAMPLE 28 | PS C:\> Test-PlatyPSFunction "File" -First "doc" 29 | File.doc 30 | .LINK 31 | http://www.fabrikam.com/extension.html 32 | .LINK 33 | Set-Item 34 | #> 35 | 36 | param( 37 | [Parameter(Mandatory=$true)] 38 | [String]$MustHave, 39 | [Switch]$Common, 40 | [Parameter(ParameterSetName="First", HelpMessage = 'First parameter help description')] 41 | [string]$First, 42 | [Parameter(ParameterSetName="Second")] 43 | [string]$Second 44 | ) 45 | 46 | Write-Output "Common: $Common - Second: $Second" 47 | -------------------------------------------------------------------------------- /src/Transform/TransformSettings.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections; 6 | using System.Globalization; 7 | using System.Management.Automation.Runspaces; 8 | 9 | namespace Microsoft.PowerShell.PlatyPS 10 | { 11 | public class TransformSettings 12 | { 13 | public string? FwLink { get; set; } 14 | public string? HelpVersion { get; set; } 15 | public CultureInfo Locale { get; set; } 16 | public Hashtable? Metadata { get; set; } 17 | public Guid? ModuleGuid { get; set; } 18 | public string? ModuleName { get; set; } 19 | public string? OnlineVersionUrl { get; set; } 20 | public bool? CreateModulePage { get; set; } 21 | public bool? DoubleDashList { get; set; } 22 | public bool? UseFullTypeName { get; set; } 23 | public PSSession? Session { get; set; } 24 | public bool? ExcludeDontShow { get; set; } 25 | 26 | public TransformSettings() 27 | { 28 | Locale = CultureInfo.CurrentCulture; 29 | } 30 | 31 | public TransformSettings(CultureInfo cultureInfo) 32 | { 33 | Locale = cultureInfo; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /docs/archive/alias-prototype.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Microsoft.PowerShell.Commands.Management.dll-Help.xml 3 | Locale: en-US 4 | Module Name: Microsoft.PowerShell.Management 5 | ms.date: 12/05/2023 6 | online version: https://docs.microsoft.com/powershell/module/microsoft.powershell.management/get-childitem?view=powershell-7.2&WT.mc_id=ps-gethelp 7 | schema: 2.0.0 8 | title: Get-ChildItem 9 | alias: [dir, gci, ls] 10 | --- 11 | # Get-ChildItem 12 | 13 | ## SYNOPSIS 14 | 15 | Gets the items and child items in one or more specified locations. 16 | 17 | ## ALIASES 18 | 19 | PowerShell includes the following aliases for `Get-ChildItem`: 20 | 21 | - All platforms: `dir`, `gci` 22 | - Windows: `ls` 23 | 24 | ## SYNTAX 25 | 26 | ### Items (Default) 27 | 28 | ``` 29 | Get-ChildItem [[-Path] ] [[-Filter] ] [-Include ] [-Exclude ] 30 | [-Recurse] [-Depth ] [-Force] [-Name] [-Attributes ] 31 | [-FollowSymlink] [-Directory] [-File] [-Hidden] [-ReadOnly] [-System] [] 32 | ``` 33 | 34 | ### LiteralItems 35 | 36 | ``` 37 | Get-ChildItem [[-Filter] ] -LiteralPath [-Include ] 38 | [-Exclude ] [-Recurse] [-Depth ] [-Force] [-Name] 39 | [-Attributes ] [-FollowSymlink] [-Directory] [-File] [-Hidden] 40 | [-ReadOnly] [-System] [] 41 | ``` 42 | -------------------------------------------------------------------------------- /test/Pester/assets/Get-MPIOSetting.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: MPIO_Cmdlets.xml 3 | Module Name: MPIO 4 | online version: https://learn.microsoft.com/powershell/module/mpio/get-mpiosetting?view=windowsserver2012-ps&wt.mc_id=ps-gethelp 5 | schema: 2.0.0 6 | title: Get-MPIOSetting 7 | --- 8 | # Get-MPIOSetting 9 | 10 | ## SYNOPSIS 11 | Gets MPIO settings. 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Get-MPIOSetting 17 | ``` 18 | 19 | ## DESCRIPTION 20 | The **Get-MPIOSetting** cmdlet gets Microsoft Multipath I/O (MPIO) settings. 21 | The settings are as follows: 22 | 23 | - PathVerificationState 24 | - PathVerificationPeriod 25 | - PDORemovePeriod 26 | - RetryCount 27 | - RetryInterval 28 | - UseCustomPathRecoveryTime 29 | - CustomPathRecoveryTime 30 | - DiskTimeoutValue 31 | 32 | You can use the **Set-MPIOSetting** cmdlet to change these values. 33 | 34 | ## EXAMPLES 35 | 36 | ### Example 1: Get MPIO settings 37 | ``` 38 | PS C:\>Get-MPIOSetting 39 | 40 | PathVerificationState : Disabled 41 | PathVerificationPeriod : 30 42 | PDORemovePeriod : 20 43 | RetryCount : 3 44 | RetryInterval : 1 45 | UseCustomPathRecoveryTime : Disabled 46 | CustomPathRecoveryTime : 40 47 | DiskTimeoutValue : 120 48 | ``` 49 | 50 | This command gets the MPIO settings. 51 | 52 | ## PARAMETERS 53 | 54 | ## INPUTS 55 | 56 | ## OUTPUTS 57 | 58 | ## NOTES 59 | 60 | ## RELATED LINKS 61 | 62 | [Set-MPIOSetting](./Set-MPIOSetting.md) 63 | -------------------------------------------------------------------------------- /src/Common/StringBuilderPool.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Concurrent; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace Microsoft.PowerShell.PlatyPS 12 | { 13 | internal class StringBuilderPool 14 | { 15 | private readonly ConcurrentBag _stringBuilders; 16 | 17 | internal int InitialCapacity { get; set; } = 5000; 18 | 19 | internal int MaximumRetainedCapacity { get; set; } = 3 * 5000; 20 | 21 | public StringBuilderPool() 22 | { 23 | _stringBuilders = new ConcurrentBag(); 24 | } 25 | 26 | internal StringBuilder Get() 27 | { 28 | if (_stringBuilders.TryTake(out StringBuilder? sb)) 29 | { 30 | return sb; 31 | } 32 | else 33 | { 34 | return new StringBuilder(InitialCapacity); 35 | } 36 | } 37 | 38 | internal bool Return(StringBuilder sb) 39 | { 40 | if(sb.Capacity > MaximumRetainedCapacity) 41 | { 42 | // Too big. Discard this one. 43 | return false; 44 | } 45 | 46 | sb.Clear(); 47 | 48 | _stringBuilders.Add(sb); 49 | 50 | return true; 51 | } 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test/Pester/InteractiveExperience.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | Describe "Interactive experience tests" { 5 | Context "Encoding parameter tab completion" { 6 | BeforeAll { 7 | $cmdletsWithEncodingParameter = Get-Command -Module Microsoft.PowerShell.PlatyPS | 8 | Where-Object { $_.Parameters['Encoding'] } 9 | $expectedEncoding = "ansi", "ascii", "bigendianunicode", "bigendianutf32", "unicode", "utf7", "utf8", "utf8BOM", "utf8NoBOM", "utf32" 10 | } 11 | 12 | It "'' has the appropriate attributes on the Encoding parameter" -TestCases $( 13 | $cmdletsWithEncodingParameter | Foreach-Object { 14 | @{ Cmdlet = $_.Name; Parameter = $_.Parameters['Encoding'] } 15 | } 16 | ) { 17 | param ($cmdlet, $parameter) 18 | $parameter.Attributes.Where({$_ -is [System.Management.Automation.ArgumentCompleterAttribute]}) | Should -Not -BeNullOrEmpty 19 | } 20 | 21 | It "tabexpansion for '' completes the Encoding parameter correctly" -TestCases $( 22 | $cmdletsWithEncodingParameter | Foreach-Object { 23 | @{ Cmdlet = $_.Name } 24 | } 25 | ) { 26 | param ($cmdlet) 27 | $cmdString = "$cmdlet -Encoding " 28 | $result = TabExpansion2 $cmdString $cmdString.Length 29 | $result.CompletionMatches.CompletionText | Should -Be $expectedEncoding 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/Microsoft.PowerShell.PlatyPS.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PlatyPS 5 | Microsoft Corporation 6 | (c) Microsoft Corporation. 7 | 8 | net472 9 | true 10 | true 11 | latest 12 | true 13 | enable 14 | true 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | True 27 | True 28 | Microsoft.PowerShell.PlatyPS.Resources.resx 29 | 30 | 31 | 32 | 33 | 34 | ResXFileCodeGenerator 35 | Microsoft.PowerShell.PlatyPS.Resources.Designer.cs 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /tools/releaseBuild/template/publish.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | stageName: '' 3 | environmentName: '' 4 | feedCredential: '' 5 | 6 | stages: 7 | - stage: ${{ parameters.stageName }} 8 | displayName: Release Microsoft.PowerShell.PlatyPS to '${{ parameters.stageName }}' 9 | condition: and(succeeded(), eq(variables['Build.Reason'], 'Manual'), startsWith(variables['Build.SourceBranch'], 'refs/heads/release')) 10 | 11 | jobs: 12 | - deployment: Publish_${{ parameters.stageName }} 13 | displayName: Release to Feed 14 | pool: 15 | name: 1ES 16 | 17 | environment: ${{ parameters.environmentName }} 18 | strategy: 19 | runOnce: 20 | deploy: 21 | steps: 22 | - download: current 23 | artifact: nuget 24 | 25 | - pwsh: | 26 | $package = (Get-ChildItem "$(Pipeline.Workspace)/nuget/Microsoft.PowerShell.PlatyPS.*.nupkg").FullName 27 | $package 28 | $vstsCommandString = "vso[task.setvariable variable=NugetPkgPath]${package}" 29 | Write-Host "sending " + $vstsCommandString 30 | Write-Host "##$vstsCommandString" 31 | displayName: Capture downloaded artifact 32 | 33 | - task: NuGetAuthenticate@0 34 | condition: ne('${{ parameters.feedUrl }}', '') 35 | 36 | - task: NuGetCommand@2 37 | displayName: 'NuGet push' 38 | inputs: 39 | command: push 40 | packagesToPush: '$(NugetPkgPath)' 41 | nuGetFeedType: external 42 | publishFeedCredentials: ${{ parameters.feedCredential }} 43 | condition: eq('${{ parameters.feedUrl }}', '') 44 | -------------------------------------------------------------------------------- /docs/archive/platyps_2.0_about_schema.md: -------------------------------------------------------------------------------- 1 | # Schema for About_ files 2 | 3 | The following sections outline schema changes for the About_ file. 4 | 5 | | Heading | Level | Required? | Count | Description | 6 | | ----------------- | ----- | --------- | ----- | ------------------------------------------------------------------------------------------------------------------------- | 7 | | Title | H1 | Y | 1 | - Title should be Sentence Case - About Topic
- Title meta data 'about_' should match the file basename | 8 | | Short Description | H2 | Y | 1 | - Should be Sentence Case | 9 | | Long Description | H2 | Y | 1 | - Should be Sentence case
- Should allow multiple Long description subtopics
- Should support subtopics at H3 or H2 | 10 | | See Also | H2 | Y | 1 | - This is required but may be empty | 11 | 12 | General notes 13 | 14 | - Should be rendered as plain text compatible with `Get-Help` 15 | - `Get-Help` bug:Synopsis 16 | - [Get-Help bug][05] 17 | - Add switch to provide Cabs or Zips. Default: cabs 18 | - Add switch to include markdown Default: off 19 | - About_ schema does not say anything about line wrapping etc. 20 | - If left as text files, then wrap at 80 columns. 21 | - But if converted to schema-based line limit isn't a problem (for the future). Still a problem 22 | for previous versions. 23 | -------------------------------------------------------------------------------- /src/MamlWriter/CommandDetails.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections.Generic; 5 | using System.Xml.Serialization; 6 | 7 | namespace Microsoft.PowerShell.PlatyPS.MAML 8 | { 9 | /// 10 | /// Represents the "details" element under a "command" element in a Powershell MAML help document. 11 | /// 12 | [XmlRoot("details", Namespace = Constants.XmlNamespace.Command)] 13 | public class CommandDetails 14 | { 15 | /// 16 | /// The Cmdlet name. 17 | /// 18 | [XmlElement("name", Namespace = Constants.XmlNamespace.Command, Order = 0)] 19 | public string Name { get; set; } = string.Empty; 20 | 21 | /// 22 | /// The command synopsis (one or more paragraphs; "maml:description/maml:para"). 23 | /// 24 | [XmlArray("description", Namespace = Constants.XmlNamespace.MAML, Order = 1)] 25 | [XmlArrayItem("para", Namespace = Constants.XmlNamespace.MAML)] 26 | public List Synopsis { get; set; } = new List(); 27 | 28 | /// 29 | /// The verb component of the Cmdlet's name. 30 | /// 31 | [XmlElement("verb", Namespace = Constants.XmlNamespace.Command, Order = 2)] 32 | public string Verb { get; set; } = string.Empty; 33 | 34 | /// 35 | /// The noun component of the Cmdlet's name. 36 | /// 37 | [XmlElement("noun", Namespace = Constants.XmlNamespace.Command, Order = 3)] 38 | public string Noun { get; set; } = string.Empty; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/MamlWriter/CommandExample.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections.Generic; 5 | using System.Xml.Serialization; 6 | 7 | namespace Microsoft.PowerShell.PlatyPS.MAML 8 | { 9 | /// 10 | /// Represents a "command:example" element in a Powershell MAML help document. 11 | /// 12 | public class CommandExample 13 | { 14 | /// 15 | /// The command details. 16 | /// 17 | [XmlElement("title", Namespace = Constants.XmlNamespace.MAML, Order = 0)] 18 | public string Title { get; set; } = string.Empty; 19 | 20 | /// 21 | /// An introduction to the example (one or more paragraphs; "maml:introduction/maml:para"). 22 | /// 23 | [XmlArray("introduction", Namespace = Constants.XmlNamespace.MAML, Order = 1)] 24 | [XmlArrayItem("para", Namespace = Constants.XmlNamespace.MAML)] 25 | public List Description { get; set; } = new List(); 26 | 27 | /// 28 | /// The example code. 29 | /// 30 | [XmlElement("code", Namespace = Constants.XmlNamespace.Dev, Order = 2)] 31 | public string Code { get; set; } = string.Empty; 32 | 33 | /// 34 | /// An introduction to the example (one or more paragraphs; "dev:remarks/maml:para"). 35 | /// 36 | [XmlArray("remarks", Namespace = Constants.XmlNamespace.Dev, Order = 3)] 37 | [XmlArrayItem("para", Namespace = Constants.XmlNamespace.MAML)] 38 | public List Remarks { get; set; } = new List(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /test/Pester/assets/PSDiagnostics.md: -------------------------------------------------------------------------------- 1 | --- 2 | Download Help Link: https://aka.ms/powershell75-help 3 | Help Version: 7.5.0.0 4 | Locale: en-US 5 | Module Guid: c61d6278-02a3-4618-ae37-a524d40a7f44 6 | Module Name: PSDiagnostics 7 | ms.date: 11/27/2018 8 | schema: 2.0.0 9 | title: PSDiagnostics Module 10 | --- 11 | # PSDiagnostics Module 12 | 13 | ## Description 14 | 15 | The PowerShell Diagnostics Module contains a set of cmdlets that enables the use of ETW 16 | tracing in PowerShell on Windows. 17 | 18 | This module is only available on the Windows platform. 19 | 20 | ## PSDiagnostics Cmdlets 21 | 22 | ### [Disable-PSTrace](Disable-PSTrace.md) 23 | Disables the Microsoft-Windows-PowerShell event provider logs. 24 | 25 | ### [Disable-PSWSManCombinedTrace](Disable-PSWSManCombinedTrace.md) 26 | Stop the logging session started by Enable-PSWSManCombinedTrace. 27 | 28 | ### [Disable-WSManTrace](Disable-WSManTrace.md) 29 | Stop the WSMan logging session started by Enable-WSManTrace. 30 | 31 | ### [Enable-PSTrace](Enable-PSTrace.md) 32 | Enables the Microsoft-Windows-PowerShell event provider logs. 33 | 34 | ### [Enable-PSWSManCombinedTrace](Enable-PSWSManCombinedTrace.md) 35 | Start a logging session with the WSMan and PowerShell providers enabled. 36 | 37 | ### [Enable-WSManTrace](Enable-WSManTrace.md) 38 | Start a logging session with the WSMan providers enabled. 39 | 40 | ### [Get-LogProperties](Get-LogProperties.md) 41 | Retrieves the properties of a Windows event log. 42 | 43 | ### [Set-LogProperties](Set-LogProperties.md) 44 | Changes the properties of a Windows event log. 45 | 46 | ### [Start-Trace](Start-Trace.md) 47 | Start an Event Trace logging session. 48 | 49 | ### [Stop-Trace](Stop-Trace.md) 50 | Stop an Event Trace logging session. 51 | 52 | -------------------------------------------------------------------------------- /src/Command/ConvertToCommandHelpCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Globalization; 7 | using System.Management.Automation; 8 | using Microsoft.PowerShell.PlatyPS.Model; 9 | 10 | namespace Microsoft.PowerShell.PlatyPS 11 | { 12 | /// 13 | /// Cmdlet to import a markdown file and convert it to a CommandHelp object. 14 | /// 15 | [Cmdlet(VerbsCommon.New, "CommandHelp")] 16 | [OutputType(typeof(Microsoft.PowerShell.PlatyPS.Model.CommandHelp[]))] 17 | public sealed class NewCommandHelpCommand : PSCmdlet 18 | { 19 | #region cmdlet parameters 20 | [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true)] 21 | [ValidateNotNullOrEmpty] 22 | public CommandInfo[] CommandInfo { get; set; } = new CommandInfo[0]; 23 | #endregion 24 | 25 | List? cmdHelpObjs = null; 26 | TransformSettings transformSettings = new(); 27 | 28 | protected override void BeginProcessing() 29 | { 30 | transformSettings = new TransformSettings() 31 | { 32 | CreateModulePage = false, 33 | DoubleDashList = false, 34 | ExcludeDontShow = true, 35 | HelpVersion = "3.0.0", 36 | Locale = CultureInfo.GetCultureInfo("en-US"), 37 | UseFullTypeName = true 38 | }; 39 | } 40 | 41 | protected override void ProcessRecord() 42 | { 43 | cmdHelpObjs = new TransformCommand(transformSettings).Transform(CommandInfo); 44 | foreach (var cHelp in cmdHelpObjs) 45 | { 46 | WriteObject(cHelp); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /test/Pester/assets/New-EmptyCommand.md: -------------------------------------------------------------------------------- 1 | --- 2 | document type: cmdlet 3 | external help file: New-EmptyCommand-Help.xml 4 | HelpUri: '' 5 | Locale: en-US 6 | Module Name: '' 7 | ms.date: 03/04/2025 8 | PlatyPS schema version: 2024-05-01 9 | title: New-EmptyCommand 10 | --- 11 | 12 | # New-EmptyCommand 13 | 14 | ## SYNOPSIS 15 | 16 | {{ Fill in the Synopsis }} 17 | 18 | ## SYNTAX 19 | 20 | ### __AllParameterSets 21 | 22 | ``` 23 | New-EmptyCommand [[-InputString] ] [] 24 | ``` 25 | 26 | ## ALIASES 27 | 28 | This cmdlet has the following aliases, 29 | {{Insert list of aliases}} 30 | 31 | ## DESCRIPTION 32 | 33 | {{ Fill in the Description }} 34 | 35 | ## EXAMPLES 36 | 37 | ### Example 1 38 | 39 | {{ Add example description here }} 40 | 41 | ## PARAMETERS 42 | 43 | ### -InputString 44 | 45 | {{ Fill InputString Description }} 46 | 47 | ```yaml 48 | Type: System.String 49 | DefaultValue: '' 50 | SupportsWildcards: false 51 | ParameterValue: [] 52 | Aliases: 53 | - InputObject 54 | - InputParameter 55 | ParameterSets: 56 | - Name: (All) 57 | Position: 0 58 | IsRequired: false 59 | ValueFromPipeline: true 60 | ValueFromPipelineByPropertyName: false 61 | ValueFromRemainingArguments: false 62 | DontShow: false 63 | AcceptedValues: [] 64 | HelpMessage: '' 65 | ``` 66 | 67 | ### CommonParameters 68 | 69 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, 70 | -InformationAction, -InformationVariable, -OutBuffer, -OutVariable, -PipelineVariable, 71 | -ProgressAction, -Verbose, -WarningAction, and -WarningVariable. For more information, see 72 | [about_CommonParameters](https://go.microsoft.com/fwlink/?LinkID=113216). 73 | 74 | ## INPUTS 75 | 76 | ### System.String 77 | 78 | ## OUTPUTS 79 | 80 | ### System.String 81 | 82 | ## NOTES 83 | 84 | ## RELATED LINKS 85 | -------------------------------------------------------------------------------- /test/Pester/MeasurePlatyPSMarkdown.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | Describe "Export-MarkdownModuleFile" { 5 | BeforeAll { 6 | $files = Get-ChildItem $PSScriptRoot/assets -filter *.md 7 | $idents = $files.FullName | Measure-PlatyPSMarkdown 8 | $goodFile1 = $idents.Where({$_.FilePath -match "get-date.md$"}) 9 | $goodFile2 = $idents.Where({$_.FilePath -match "Compare-CommandHelp.md$"}) 10 | $mfPath = Import-MarkdownModuleFile $PSScriptRoot/assets/Microsoft.PowerShell.Archive.md | 11 | Export-MarkdownModuleFile -outputFolder $TESTDRIVE 12 | } 13 | 14 | It "Should identify all the '' assets" -TestCases @( 15 | @{ fileType = "unknown"; expectedCount = 2 } 16 | @{ fileType = "CommandHelp"; expectedCount = 42 } 17 | @{ fileType = "ModuleFile"; expectedCount = 16 } 18 | @{ fileType = "V1Schema"; expectedCount = 53 } 19 | @{ fileType = "V2Schema"; expectedCount = 5 } 20 | ) { 21 | param ($fileType, $expectedCount) 22 | $idents.Where({($_.FileType -band $fileType) -eq $fileType}).Count | Should -Be $expectedCount 23 | } 24 | 25 | It "Should have proper diagnostics for get-date.md" { 26 | $goodFile1.DiagnosticMessages.Count | Should -Be 4 27 | $goodFile1.FileType | Should -match "v1schema" 28 | $goodFile1.DiagnosticMessages[-1].Message | Should -Match "PlatyPS.*schema.*marking as v1" 29 | } 30 | 31 | It "Should have proper diagnostics for Compare-CommandHelp.md" { 32 | $goodFile2.DiagnosticMessages.Count | Should -Be 2 33 | ($goodFile2.FileType -band "v2schema") -eq "v2schema" | Should -Be $true 34 | $goodFile2.DiagnosticMessages[-1].Message | Should -Be "document type found: cmdlet" 35 | } 36 | 37 | It "Should recognise a V2 module file" { 38 | $v2ModuleFile = Measure-PlatyPSMarkdown -Path $mfPath.Fullname 39 | $v2ModuleFile.Filetype | Should -Be "ModuleFile, V2Schema" 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct][02]. 4 | 5 | We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and 6 | healthy community. 7 | 8 | ## Our Standards 9 | 10 | Examples of behavior that contributes to a positive environment for our community include: 11 | 12 | - Demonstrating empathy and kindness toward other people 13 | - Being respectful of differing opinions, viewpoints, and experiences 14 | - Giving and gracefully accepting constructive feedback 15 | - Accepting responsibility and apologizing to those affected by our mistakes, and learning from the 16 | experience 17 | - Focusing on what's best not just for us as individuals, but for the overall community 18 | 19 | Examples of unacceptable behavior include: 20 | 21 | - Disruptive behavior 22 | - Submitting spam comments, issues, or pull requests 23 | - Defacing or vandalizing the project, repository, content, or documentation 24 | - Intentionally introducing security vulnerabilities 25 | - Disrespectful behavior 26 | - Trolling, insulting or derogatory comments, and personal or political attacks 27 | - Public or private harassment 28 | - Publishing others' private information, such as a physical or email address, without their 29 | explicit permission 30 | - The use of sexualized language or imagery, and sexual attention or advances of any kind 31 | - Other conduct that could reasonably be considered inappropriate in a professional setting 32 | 33 | ## Resources 34 | 35 | - [Microsoft Open Source Code of Conduct][02] 36 | - [Microsoft Code of Conduct FAQ][03] 37 | - Contact [opencode@microsoft.com][04] with questions or concerns 38 | - Employees can reach out at [aka.ms/opensource/moderation-support][01] 39 | 40 | 41 | [01]: https://aka.ms/opensource/moderation-support 42 | [02]: https://opensource.microsoft.com/codeofconduct/ 43 | [03]: https://opensource.microsoft.com/codeofconduct/faq/ 44 | [04]: mailto:opencode@microsoft.com 45 | -------------------------------------------------------------------------------- /src/Transform/TransformCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections.ObjectModel; 5 | using System.Collections.Generic; 6 | using System.Management.Automation; 7 | using Microsoft.PowerShell.PlatyPS.Model; 8 | 9 | namespace Microsoft.PowerShell.PlatyPS 10 | { 11 | internal class TransformCommand : TransformBase 12 | { 13 | public TransformCommand(TransformSettings settings) : base(settings) 14 | { 15 | } 16 | 17 | internal List Transform(IEnumerable command) 18 | { 19 | List cmdHelpList = new(); 20 | foreach(var cmd in command) 21 | { 22 | cmdHelpList.Add(Transform(cmd)); 23 | } 24 | return cmdHelpList; 25 | } 26 | 27 | internal CommandHelp Transform(CommandInfo command) 28 | { 29 | return ConvertCmdletInfo(command); 30 | } 31 | 32 | internal Collection Transform(string command) 33 | { 34 | Collection cmdHelp = new(); 35 | Collection cmdletInfos = PowerShellAPI.GetCommandInfo(command, Settings.Session); 36 | 37 | foreach(var cmdletInfo in cmdletInfos) 38 | { 39 | cmdHelp.Add(ConvertCmdletInfo(cmdletInfo)); 40 | } 41 | 42 | return cmdHelp; 43 | } 44 | 45 | internal override Collection Transform(string[] commandNames) 46 | { 47 | Collection cmdHelp = new(); 48 | 49 | foreach (var command in commandNames) 50 | { 51 | Collection cmdletInfos = PowerShellAPI.GetCommandInfo(command, Settings.Session); 52 | 53 | foreach (var cmdletInfo in cmdletInfos) 54 | { 55 | cmdHelp.Add(ConvertCmdletInfo(cmdletInfo)); 56 | } 57 | } 58 | 59 | return cmdHelp; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /test/Pester/assets/Exit-PSHostProcess.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: System.Management.Automation.dll-Help.xml 3 | Locale: en-US 4 | Module Name: Microsoft.PowerShell.Core 5 | ms.date: 11/06/2020 6 | online version: https://learn.microsoft.com/powershell/module/microsoft.powershell.core/exit-pshostprocess?view=powershell-7.4&WT.mc_id=ps-gethelp 7 | schema: 2.0.0 8 | title: Exit-PSHostProcess 9 | --- 10 | # Exit-PSHostProcess 11 | 12 | ## SYNOPSIS 13 | Closes an interactive session with a local process. 14 | 15 | ## SYNTAX 16 | 17 | ``` 18 | Exit-PSHostProcess [] 19 | ``` 20 | 21 | ## DESCRIPTION 22 | 23 | The `Exit-PSHostProcess` cmdlet closes an interactive session with a local process that you have 24 | opened by running the `Enter-PSHostProcess` cmdlet. You run the `Exit-PSHostProcess` cmdlet from 25 | within the process, when you are finished debugging or troubleshooting a script that is running 26 | within a process. Beginning in PowerShell 6.2, this cmdlet is supported on non-Windows platforms. 27 | 28 | ## EXAMPLES 29 | 30 | ### Example 1: Exit a process 31 | 32 | ``` 33 | [Process:1520]: PS> Exit-PSHostProcess 34 | PS> 35 | ``` 36 | 37 | In this example, you have been working in an active process to debug a script running in a runspace 38 | in the process, as described in `Enter-PSHostProcess`. After you type the `exit` command to exit the 39 | debugger, run the `Exit-PSHostProcess` cmdlet to close your interactive session with the process. 40 | The cmdlet closes your session in the process, and returns you to the `PS C:\>` prompt. 41 | 42 | ## PARAMETERS 43 | 44 | ### CommonParameters 45 | 46 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, 47 | -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, 48 | -WarningAction, and -WarningVariable. For more information, see 49 | [about_CommonParameters](https://go.microsoft.com/fwlink/?LinkID=113216). 50 | 51 | ## INPUTS 52 | 53 | ## OUTPUTS 54 | 55 | ## NOTES 56 | 57 | ## RELATED LINKS 58 | 59 | [Enter-PSHostProcess](Enter-PSHostProcess.md) 60 | 61 | -------------------------------------------------------------------------------- /src/Common/YamlLayoutSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using YamlDotNet.Core; 4 | using YamlDotNet.Core.Events; 5 | using YamlDotNet.Serialization; 6 | 7 | namespace Microsoft.PowerShell.PlatyPS 8 | { 9 | public class LayoutSequenceStyle : IYamlTypeConverter 10 | { 11 | public bool Accepts(Type type) 12 | { 13 | bool fromString = typeof(IEnumerable).IsAssignableFrom(type); 14 | bool fromObject = typeof(IEnumerable).IsAssignableFrom(type); 15 | return fromString || fromObject; 16 | } 17 | 18 | public object ReadYaml(IParser parser, Type type) 19 | { 20 | throw new NotImplementedException(); 21 | } 22 | 23 | public void WriteYaml(IEmitter emitter, object? value, Type type) 24 | { 25 | if (value is null) 26 | { 27 | throw new ArgumentException("object to serialize must not be null"); 28 | } 29 | 30 | IEnumerable sequence; 31 | if (value is IEnumerable collection) 32 | { 33 | List stringCollection = new(); 34 | foreach (var item in collection) 35 | { 36 | if (item is not null) 37 | { 38 | stringCollection.Add(item.ToString()); 39 | } 40 | else 41 | { 42 | stringCollection.Add(string.Empty); 43 | } 44 | } 45 | sequence = stringCollection; 46 | } 47 | else 48 | { 49 | sequence = (IEnumerable)value; 50 | } 51 | 52 | emitter.Emit(new SequenceStart(default, default, false, SequenceStyle.Flow)); 53 | 54 | foreach (var item in sequence) 55 | { 56 | emitter.Emit(new Scalar(default, item)); 57 | } 58 | 59 | emitter.Emit(new SequenceEnd()); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /test/Pester/assets/PSReadLine.md: -------------------------------------------------------------------------------- 1 | --- 2 | Download Help Link: https://aka.ms/powershell75-help 3 | Help Version: 7.5.0.0 4 | Locale: en-US 5 | Module Guid: 5714753b-2afd-4492-a5fd-01d9e2cff8b5 6 | Module Name: PSReadLine 7 | ms.date: 12/04/2023 8 | schema: 2.0.0 9 | title: PSReadLine 10 | --- 11 | # PSReadLine Module 12 | 13 | ## Description 14 | 15 | The PSReadLine module contains cmdlets that let you customize the command-line 16 | editing environment in PowerShell. 17 | 18 | There have been many updates to PSReadLine since the version that ships in 19 | Windows PowerShell 5.1. 20 | 21 | - PowerShell 7.4.0 ships with PSReadLine 2.3.4 22 | - PowerShell 7.3.0 ships with PSReadLine 2.2.6 23 | - PowerShell 7.2.5 ships with PSReadLine 2.1.0 24 | - PowerShell 7.0.11 ships with PSReadLine 2.0.4 25 | - PowerShell 5.1 ships with PSReadLine 2.0.0 26 | 27 | These articles document version 2.3.4 of PSReadLine. 28 | 29 | > [!NOTE] 30 | > Beginning with PowerShell 7.0, PowerShell skips auto-loading PSReadLine on 31 | > Windows if a screen reader program is detected. Currently, PSReadLine doesn't 32 | > work well with the screen readers. The default rendering and formatting of 33 | > PowerShell 7.0 on Windows works properly. You can manually load the module if 34 | > necessary. 35 | 36 | ## PSReadLine Cmdlets 37 | 38 | ### [PSConsoleHostReadLine](PSConsoleHostReadLine.md) 39 | The main entry point for PSReadLine. 40 | 41 | ### [Get-PSReadLineKeyHandler](Get-PSReadLineKeyHandler.md) 42 | Gets the bound key functions for the PSReadLine module. 43 | 44 | ### [Get-PSReadLineOption](Get-PSReadLineOption.md) 45 | Gets values for the options that can be configured. 46 | 47 | ### [PSConsoleHostReadLine](PSConsoleHostReadLine.md) 48 | This function is the main entry point for PSReadLine. 49 | 50 | ### [Remove-PSReadLineKeyHandler](Remove-PSReadLineKeyHandler.md) 51 | Removes a key binding. 52 | 53 | ### [Set-PSReadLineKeyHandler](Set-PSReadLineKeyHandler.md) 54 | Binds keys to user-defined or PSReadLine key handler functions. 55 | 56 | ### [Set-PSReadLineOption](Set-PSReadLineOption.md) 57 | Customizes the behavior of command line editing in **PSReadLine**. 58 | -------------------------------------------------------------------------------- /test/Pester/MarkdownYaml.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | Describe "Create valid yaml" { 5 | BeforeAll { 6 | $moduleName = "Microsoft.PowerShell.Core" 7 | New-Item -Type Directory -Path "${TESTDRIVE}/markdown" 8 | New-Item -Type Directory -Path "${TESTDRIVE}/yaml" 9 | New-Item -Type Directory -Path "${TESTDRIVE}/psdocs" 10 | Import-Module "${PSScriptRoot}/PlatyPS.Test.psm1" 11 | $verbs = get-verb 12 | try { 13 | push-location "${TESTDRIVE}/psdocs" 14 | try { 15 | git clone "https://github.com/PowerShell/PowerShell-Docs" 16 | push-location "PowerShell-Docs/reference/7.4/${moduleName}" 17 | $mdfiles = Get-ChildItem -filter *.md | Where-Object { $verbs.Verb -Contains ($_.name.split("-")[0]) } 18 | Copy-Item $mdfiles "${TESTDRIVE}/markdown" 19 | } 20 | finally { 21 | pop-location 22 | } 23 | } 24 | finally { 25 | pop-location 26 | } 27 | 28 | $testCases = Get-ChildItem -Path "${TESTDRIVE}/markdown" -Filter *.md | 29 | foreach-object { @{ Path = $_.FullName; Name = $_.Name } } 30 | } 31 | 32 | It "Converting '' to Yaml creates readable file." -TestCases $testCases { 33 | param ( $Path, $Name ) 34 | 35 | $yamlFileName = $Name -replace ".md",".yml" 36 | try { 37 | $ch = $Path | Import-MarkdownCommandHelp 38 | $ch | Export-YamlCommandHelp -outputFolder "${TESTDRIVE}/yaml" 39 | } 40 | catch { 41 | $_.Exception.Message | Should -BeNullOrEmpty 42 | } 43 | 44 | try { 45 | $yamlFilePath = [io.path]::Combine("${TESTDRIVE}/yaml", $moduleName, $yamlFileName) 46 | $result = $yamlFilePath | Import-CommandYaml 47 | $result | Should -Not -BeNullOrEmpty 48 | $result.GetType().Name | Should -Be "hashtable" 49 | } 50 | catch { 51 | $_.Exception.Message | Should -BeNullOrEmpty 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/MamlWriter/HelpItems.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Xml; 8 | using System.Xml.Serialization; 9 | 10 | namespace Microsoft.PowerShell.PlatyPS.MAML 11 | { 12 | /// 13 | /// The root ("helpItems") XML element in a Powershell MAML help file. 14 | /// 15 | [XmlRoot("helpItems", Namespace = Constants.XmlNamespace.Root)] 16 | public class HelpItems 17 | { 18 | /// 19 | /// No idea what the point of this atribute is. 20 | /// 21 | [XmlAttribute("schema")] 22 | public string Schema { get; set; } = "maml"; 23 | 24 | /// 25 | /// Command documentation. 26 | /// 27 | [XmlElement("command", Namespace = Constants.XmlNamespace.Command)] 28 | public List Commands { get; set; } = new List(); 29 | 30 | /// 31 | /// Write the help to the specified . 32 | /// 33 | /// 34 | /// 35 | /// 36 | public void WriteTo(TextWriter writer) 37 | { 38 | if (writer == null) 39 | throw new ArgumentNullException(nameof(writer)); 40 | 41 | new XmlSerializer(GetType()).Serialize( 42 | writer, this, Constants.XmlNamespace.GetStandardPrefixes() 43 | ); 44 | } 45 | 46 | /// 47 | /// Write the help to the specified . 48 | /// 49 | /// 50 | /// 51 | /// 52 | public void WriteTo(XmlWriter writer) 53 | { 54 | if (writer == null) 55 | throw new ArgumentNullException(nameof(writer)); 56 | 57 | new XmlSerializer(GetType()).Serialize( 58 | writer, this, Constants.XmlNamespace.GetStandardPrefixes() 59 | ); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /test/Pester/assets/CimCmdlets.md: -------------------------------------------------------------------------------- 1 | --- 2 | Download Help Link: https://aka.ms/powershell75-help 3 | Help Version: 7.5.0.0 4 | Locale: en-US 5 | Module Guid: fb6cc51d-c096-4b38-b78d-0fed6277096a 6 | Module Name: CimCmdlets 7 | ms.date: 02/20/2019 8 | schema: 2.0.0 9 | title: CimCmdlets Module 10 | --- 11 | # CimCmdlets Module 12 | 13 | ## Description 14 | 15 | Contains cmdlets that interact with Common Information Model (CIM) Servers like the Windows 16 | Management Instrumentation (WMI) service. 17 | 18 | This module is only available on the Windows platform. 19 | 20 | ## CimCmdlets Cmdlets 21 | 22 | ### [Export-BinaryMiLog](Export-BinaryMiLog.md) 23 | Creates a binary encoded representation of an object or objects and stores it in a file. 24 | 25 | ### [Get-CimAssociatedInstance](Get-CimAssociatedInstance.md) 26 | Retrieves the CIM instances that are connected to a specific CIM instance by an association. 27 | 28 | ### [Get-CimClass](Get-CimClass.md) 29 | Gets a list of CIM classes in a specific namespace. 30 | 31 | ### [Get-CimInstance](Get-CimInstance.md) 32 | Gets the CIM instances of a class from a CIM server. 33 | 34 | ### [Get-CimSession](Get-CimSession.md) 35 | Gets the CIM session objects from the current session. 36 | 37 | ### [Import-BinaryMiLog](Import-BinaryMiLog.md) 38 | Used to re-create the saved objects based on the contents of an export file. 39 | 40 | ### [Invoke-CimMethod](Invoke-CimMethod.md) 41 | Invokes a method of a CIM class. 42 | 43 | ### [New-CimInstance](New-CimInstance.md) 44 | Creates a CIM instance. 45 | 46 | ### [New-CimSession](New-CimSession.md) 47 | Creates a CIM session. 48 | 49 | ### [New-CimSessionOption](New-CimSessionOption.md) 50 | Specifies advanced options for the `New-CimSession` cmdlet. 51 | 52 | ### [Register-CimIndicationEvent](Register-CimIndicationEvent.md) 53 | Subscribes to indications using a filter expression or a query expression. 54 | 55 | ### [Remove-CimInstance](Remove-CimInstance.md) 56 | Removes a CIM instance from a computer. 57 | 58 | ### [Remove-CimSession](Remove-CimSession.md) 59 | Removes one or more CIM sessions. 60 | 61 | ### [Set-CimInstance](Set-CimInstance.md) 62 | Modifies a CIM instance on a CIM server by calling the **ModifyInstance** method of the CIM class. 63 | -------------------------------------------------------------------------------- /src/MamlWriter/Constants.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Xml.Serialization; 5 | 6 | namespace Microsoft.PowerShell.PlatyPS.MAML 7 | { 8 | /// 9 | /// MAML-related constants. 10 | /// 11 | public static class Constants 12 | { 13 | /// 14 | /// In MAML if a cmdlet does not have common parameters, this is included as a parameter(?). 15 | /// 16 | public const string NoCommonParameter = "NoCommonParameter"; 17 | 18 | /// 19 | /// MAML-related XML namespace constants. 20 | /// 21 | public static class XmlNamespace 22 | { 23 | /// 24 | /// The default namespace URI for the root ("helpItems") element. 25 | /// 26 | public const string Root = "http://msh"; 27 | 28 | /// 29 | /// The "maml" XML namespace URI. 30 | /// 31 | public const string MAML = "http://schemas.microsoft.com/maml/2004/10"; 32 | 33 | /// 34 | /// The "command" XML namespace URI. 35 | /// 36 | public const string Command = "http://schemas.microsoft.com/maml/dev/command/2004/10"; 37 | 38 | /// 39 | /// The "dev" XML namespace URI. 40 | /// 41 | public const string Dev = "http://schemas.microsoft.com/maml/dev/2004/10"; 42 | 43 | /// 44 | /// Create a with standard prefixes for well-known namespaces. 45 | /// 46 | /// 47 | /// The configured . 48 | /// 49 | public static XmlSerializerNamespaces GetStandardPrefixes() 50 | { 51 | XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces(); 52 | namespaces.Add("maml", MAML); 53 | namespaces.Add("command", Command); 54 | namespaces.Add("dev", Dev); 55 | 56 | return namespaces; 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /test/Pester/ImportYamlCommandHelp.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | Describe "Import-YamlCommandHelp tests" { 5 | BeforeAll { 6 | $mdFile = "${psscriptroot}/assets/get-date.md" 7 | $yamlFile = "${psscriptroot}/assets/get-date.yml" 8 | $mdch = Import-MarkdownCommandHelp "$mdFile" 9 | $ymlch = Import-YamlCommandHelp $yamlFile 10 | } 11 | 12 | Context "Basic Tests" { 13 | It "should be equivalent between yaml and markdown" { 14 | $mdch -eq $ymlch | Should -Be $true 15 | } 16 | 17 | It "should be able to pipe to import" { 18 | $yml = $yamlFile | Import-YamlCommandHelp 19 | $yml -eq $ymlch | Should -Be $true 20 | } 21 | 22 | It "should be able to pipe multiple files" { 23 | $yml = $yamlFile,$yamlFile,$yamlFile | Import-YamlCommandHelp 24 | $yml.Count | Should -Be 3 25 | } 26 | 27 | It "should handle literal paths" { 28 | $literalPath = "${testDrive}/[get-date].yml" 29 | copy-item $yamlFile $literalPath 30 | $yml = Import-YamlCommandHelp -LiteralPath $literalPath 31 | $yml -eq $mdch | Should -Be $true 32 | } 33 | 34 | It "Should return a dictionary with -AsDictionary" { 35 | $yml = Import-YamlCommandHelp -Path $yamlFile -AsDictionary 36 | $yml | Should -BeOfType "System.Collections.Specialized.OrderedDictionary" 37 | } 38 | } 39 | 40 | Context "yaml as dictionary" { 41 | BeforeAll { 42 | $yamlDict = Import-YamlCommandHelp -Path $yamlFile -AsDictionary 43 | } 44 | 45 | It "Should have the '' property" -TestCases $( 46 | @{ Name = 'metadata' } 47 | @{ Name = 'title' } 48 | @{ Name = 'synopsis' } 49 | @{ Name = 'syntaxes' } 50 | @{ Name = 'aliases' } 51 | @{ Name = 'description' } 52 | @{ Name = 'examples' } 53 | @{ Name = 'parameters' } 54 | @{ Name = 'inputs' } 55 | @{ Name = 'outputs' } 56 | @{ Name = 'notes' } 57 | @{ Name = 'links' } 58 | ) { 59 | param ($name) 60 | $yamlDict.Contains($name) | Should -Be $true 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /test/Pester/assets/CimCmdlets2.md: -------------------------------------------------------------------------------- 1 | --- 2 | Download Help Link: https://aka.ms/powershell74-help 3 | Help Version: 7.4.0.0 4 | Locale: en-US 5 | Module Guid: fb6cc51d-c096-4b38-b78d-0fed6277096a 6 | Module Name: CimCmdlets2 7 | ms.date: 02/20/2019 8 | schema: 2.0.0 9 | title: CimCmdlets Module 10 | isPreview: true 11 | foo: 12 | - bar 13 | test: true 14 | --- 15 | # CimCmdlets2 Module 16 | 17 | ## Description 18 | 19 | Contains cmdlets that interact with Common Information Model (CIM) Servers like the Windows 20 | Management Instrumentation (WMI) service. 21 | 22 | This module is only available on the Windows platform. 23 | 24 | ## CimCmdlets Cmdlets 25 | 26 | ### [Export-BinaryMiLog](Export-BinaryMiLog.md) 27 | 28 | Creates a binary encoded representation of an object or objects and stores it in a file. 29 | 30 | ### [Get-CimAssociatedInstance](Get-CimAssociatedInstance.md) 31 | 32 | Retrieves the CIM instances that are connected to a specific CIM instance by an association. 33 | 34 | ### [Get-CimClass](Get-CimClass.md) 35 | 36 | Gets a list of CIM classes in a specific namespace. 37 | 38 | ### [Get-CimInstance](Get-CimInstance.md) 39 | 40 | Gets the CIM instances of a class from a CIM server. 41 | 42 | ### [Get-CimSession](Get-CimSession.md) 43 | 44 | Gets the CIM session objects from the current session. 45 | 46 | ### [Import-BinaryMiLog](Import-BinaryMiLog.md) 47 | 48 | Used to re-create the saved objects based on the contents of an export file. 49 | 50 | ### [Invoke-CimMethod](Invoke-CimMethod.md) 51 | 52 | Invokes a method of a CIM class. 53 | 54 | ### [New-CimInstance](New-CimInstance.md) 55 | 56 | Creates a CIM instance. 57 | 58 | ### [New-CimSession](New-CimSession.md) 59 | 60 | Creates a CIM session. 61 | 62 | ### [New-CimSessionOption](New-CimSessionOption.md) 63 | 64 | Specifies advanced options for the `New-CimSession` cmdlet. 65 | 66 | ### [Register-CimIndicationEvent](Register-CimIndicationEvent.md) 67 | 68 | Subscribes to indications using a filter expression or a query expression. 69 | 70 | ### [Remove-CimInstance](Remove-CimInstance.md) 71 | 72 | Removes a CIM instance from a computer. 73 | 74 | ### [Remove-CimSession](Remove-CimSession.md) 75 | 76 | Removes one or more CIM sessions. 77 | 78 | ### [Set-CimInstance](Set-CimInstance.md) 79 | 80 | Modifies a CIM instance on a CIM server by calling the **ModifyInstance** method of the CIM class. 81 | -------------------------------------------------------------------------------- /test/Pester/assets/Microsoft.WSMan.Management.md: -------------------------------------------------------------------------------- 1 | --- 2 | Download Help Link: https://aka.ms/powershell75-help 3 | Help Version: 7.5.0.0 4 | Locale: en-US 5 | Module Guid: 766204a6-330e-4263-a7ab-46c87afc366c 6 | Module Name: Microsoft.WSMan.Management 7 | ms.date: 03/01/2019 8 | schema: 2.0.0 9 | title: Microsoft.WSMan.Management 10 | --- 11 | # Microsoft.WSMan.Management Module 12 | 13 | ## Description 14 | 15 | This section contains the help topics for the cmdlets that are installed with PowerShell 16 | Microsoft.WSMan.Management module. The WSMan module contains cmdlets and providers that manage the 17 | WS-Management protocol in PowerShell. 18 | 19 | This module is only available on the Windows platform. 20 | 21 | ## Microsoft.WSMan.Management Cmdlets 22 | 23 | ### [Connect-WSMan](Connect-WSMan.md) 24 | Connects to the WinRM service on a remote computer. 25 | 26 | ### [Disable-WSManCredSSP](Disable-WSManCredSSP.md) 27 | Disables CredSSP authentication on a computer. 28 | 29 | ### [Disconnect-WSMan](Disconnect-WSMan.md) 30 | Disconnects the client from the WinRM service on a remote computer. 31 | 32 | ### [Enable-WSManCredSSP](Enable-WSManCredSSP.md) 33 | Enables CredSSP authentication on a computer. 34 | 35 | ### [Get-WSManCredSSP](Get-WSManCredSSP.md) 36 | Gets the Credential Security Support Provider-related configuration for the client. 37 | 38 | ### [Get-WSManInstance](Get-WSManInstance.md) 39 | Displays management information for a resource instance specified by a Resource URI. 40 | 41 | ### [Invoke-WSManAction](Invoke-WSManAction.md) 42 | Invokes an action on the object that is specified by the Resource URI and by the selectors. 43 | 44 | ### [New-WSManInstance](New-WSManInstance.md) 45 | Creates a new instance of a management resource. 46 | 47 | ### [New-WSManSessionOption](New-WSManSessionOption.md) 48 | Creates session option hash table to use as input parameters for WS-Management cmdlets. 49 | 50 | ### [Remove-WSManInstance](Remove-WSManInstance.md) 51 | Deletes a management resource instance. 52 | 53 | ### [Set-WSManInstance](Set-WSManInstance.md) 54 | Modifies the management information that is related to a resource. 55 | 56 | ### [Set-WSManQuickConfig](Set-WSManQuickConfig.md) 57 | Configures the local computer for remote management. 58 | 59 | ### [Test-WSMan](Test-WSMan.md) 60 | Tests whether the WinRM service is running on a local or remote computer. 61 | 62 | -------------------------------------------------------------------------------- /src/Diagnostics/Diagnostics.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | 8 | namespace Microsoft.PowerShell.PlatyPS.Model 9 | { 10 | public class Diagnostics 11 | { 12 | /// 13 | /// The name of the file to associate with the messages 14 | /// 15 | public string FileName { get; set; } 16 | 17 | /// 18 | /// Whether any of the diagnostics messages are errors. 19 | /// 20 | public bool HadErrors { get; set; } 21 | 22 | public List Messages { get; set; } 23 | public Diagnostics() 24 | { 25 | FileName = string.Empty; 26 | Messages = new List(); 27 | HadErrors = false; 28 | } 29 | 30 | public Diagnostics(string fileName) 31 | { 32 | FileName = fileName; 33 | Messages = new List(); 34 | HadErrors = false; 35 | } 36 | 37 | public bool TryAddDiagnostic(DiagnosticMessage message) 38 | { 39 | try 40 | { 41 | Messages.Add(message); 42 | if (message.Severity == DiagnosticSeverity.Error) 43 | { 44 | HadErrors = true; 45 | } 46 | return true; 47 | } 48 | catch // Any exception is a failure to add the message 49 | { 50 | return false; 51 | } 52 | } 53 | public bool TryAddDiagnostic(DiagnosticMessageSource source, string message, DiagnosticSeverity severity, string identifier, int line) 54 | { 55 | try 56 | { 57 | var diagnostic = new DiagnosticMessage(source, message, severity, identifier, line); 58 | Messages.Add(diagnostic); 59 | if (diagnostic.Severity == DiagnosticSeverity.Error) 60 | { 61 | HadErrors = true; 62 | } 63 | return true; 64 | } 65 | catch // Any exception is a failure to add the message 66 | { 67 | return false; 68 | } 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /src/MarkdownReader/ModuleCommandInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | 6 | namespace Microsoft.PowerShell.PlatyPS 7 | { 8 | public class ModuleCommandInfo : IEquatable 9 | { 10 | public string Name { get; set; } 11 | public string Link { get; set; } 12 | public string Description { get; set; } 13 | public ModuleCommandInfo() 14 | { 15 | Name = string.Empty; 16 | Link = string.Empty; 17 | Description = string.Empty; 18 | } 19 | 20 | public ModuleCommandInfo(ModuleCommandInfo mci) 21 | { 22 | Name = mci.Name; 23 | Link = mci.Link; 24 | Description = mci.Description; 25 | } 26 | 27 | public override int GetHashCode() 28 | { 29 | return Name.GetHashCode() ^ Link.GetHashCode() ^ Description.GetHashCode(); 30 | } 31 | 32 | public bool Equals(ModuleCommandInfo other) 33 | { 34 | if (other is null) 35 | { 36 | return false; 37 | } 38 | 39 | return string.Compare(Name, other.Name) == 0 && 40 | string.Compare(Link, other.Link) == 0 && 41 | string.Compare(Description, other.Description) == 0; 42 | } 43 | 44 | public override bool Equals(object other) 45 | { 46 | if (other is null) 47 | { 48 | return false; 49 | } 50 | 51 | if (other is ModuleCommandInfo info2) 52 | { 53 | return Equals(info2); 54 | } 55 | 56 | return false; 57 | } 58 | 59 | public static bool operator == (ModuleCommandInfo info1, ModuleCommandInfo info2) 60 | { 61 | if (info1 is not null && info2 is not null) 62 | { 63 | return info1.Equals(info2); 64 | } 65 | 66 | return false; 67 | } 68 | 69 | public static bool operator !=(ModuleCommandInfo info1, ModuleCommandInfo info2) 70 | { 71 | if (info1 is not null && info2 is not null) 72 | { 73 | return ! info1.Equals(info2); 74 | } 75 | 76 | return false; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/MamlWriter/RelatedLink.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Xml.Serialization; 4 | 5 | namespace Microsoft.PowerShell.PlatyPS.MAML 6 | { 7 | /// 8 | /// Represents a "command:syntaxItem" element in a Powershell MAML help document. 9 | /// 10 | /// Set-Content 11 | /// 12 | /// 13 | /// 14 | public class NavigationLink : IEquatable 15 | { 16 | /// 17 | /// The command name for this syntax. 18 | /// 19 | [XmlElement("linkText", Namespace = Constants.XmlNamespace.MAML, Order = 0)] 20 | public string LinkText { get; set; } = string.Empty; 21 | 22 | /// 23 | /// The command parameters associated with this syntax. 24 | /// 25 | [XmlElement("uri", Namespace = Constants.XmlNamespace.Command, Order = 1)] 26 | public string Uri { get; set; } = string.Empty; 27 | 28 | public bool Equals(NavigationLink other) 29 | { 30 | if (other is null) 31 | return false; 32 | 33 | return (string.Compare(LinkText, other.LinkText) == 0 && string.Compare(Uri, other.Uri) == 0); 34 | } 35 | 36 | public override bool Equals(object other) 37 | { 38 | if (other is NavigationLink link2) 39 | { 40 | return Equals(link2); 41 | } 42 | 43 | return false; 44 | } 45 | 46 | public override int GetHashCode() 47 | { 48 | // Valid? 49 | return LinkText.GetHashCode() ^ Uri.GetHashCode(); 50 | } 51 | 52 | public static bool operator == (NavigationLink link1, NavigationLink link2) 53 | { 54 | if (link1 is not null && link2 is not null) 55 | { 56 | return link1.Equals(link2); 57 | } 58 | 59 | return false; 60 | } 61 | 62 | public static bool operator != (NavigationLink link1, NavigationLink link2) 63 | { 64 | if (link1 is not null && link2 is not null) 65 | { 66 | return ! link1.Equals(link2); 67 | } 68 | 69 | return false; 70 | } 71 | 72 | } 73 | 74 | 75 | 76 | } 77 | -------------------------------------------------------------------------------- /test/Pester/TestMarkdownCommandHelp.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | Describe "Test-MarkdownCommandHelp Tests" { 5 | It "Returns a boolean" { 6 | $result = Test-MarkdownCommandHelp ([io.path]::Combine("$PSScriptRoot", "assets", "get-date.md")) 7 | $result | Should -BeOfType boolean 8 | } 9 | 10 | It "Should report true with a good markdown file" { 11 | $result = Test-MarkdownCommandHelp ([io.path]::Combine("$PSScriptRoot", "assets","get-date.md")) 12 | $result | Should -Be $true 13 | } 14 | 15 | It "Should report false with a bad markdown file" { 16 | $result = Test-MarkdownCommandHelp ([io.path]::Combine("$PSScriptRoot","assets","bad-commandhelp.md")) 17 | $result | Should -Be $false 18 | } 19 | 20 | It "Should report details with the -DetailView parameter" { 21 | $result = Test-MarkdownCommandHelp ([io.path]::Combine("$PSScriptRoot","assets","get-date.md")) -DetailView 22 | $result | Should -BeOfType Microsoft.PowerShell.PlatyPS.MarkdownCommandHelpValidationResult 23 | $result.Path | Should -BeOfType string 24 | $result.Path | Should -Exist 25 | $result.IsValid | Should -BeOfType bool 26 | ,$result.Messages | Should -BeOfType "System.Collections.Generic.List[string]" 27 | } 28 | 29 | It "Should report no failures in the details with a good markdown file" { 30 | $result = Test-MarkdownCommandHelp ([io.path]::Combine("$PSScriptRoot","assets","get-date.md")) -DetailView 31 | $result.Messages | Should -Not -cMatch "FAIL" 32 | } 33 | 34 | It "Should report have '' when viewing details of a good markdown file" -TestCases @( 35 | @{ line = 'PASS: First element is a thematic break' }, 36 | @{ line = 'PASS: SYNOPSIS found.' }, 37 | @{ line = 'PASS: SYNTAX found.' }, 38 | @{ line = 'PASS: DESCRIPTION found.' }, 39 | @{ line = 'PASS: EXAMPLES found.' }, 40 | @{ line = 'PASS: PARAMETERS found.' }, 41 | @{ line = 'PASS: INPUTS found.' }, 42 | @{ line = 'PASS: OUTPUTS found.' }, 43 | @{ line = 'PASS: NOTES found.' }, 44 | @{ line = 'PASS: RELATED LINKS found.' } 45 | ) { 46 | param ($line) 47 | $result = Test-MarkdownCommandHelp ([io.path]::Combine("$PSScriptRoot", "assets", "get-date.md")) -DetailView 48 | $result.Messages | Should -Contain $line 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/MamlWriter/MamlConstants.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | namespace Microsoft.PowerShell.PlatyPS.Model 9 | { 10 | // MAML CONSTANTS 11 | internal static partial class Constants 12 | { 13 | internal const string MamlCommandCommandTag = "command:command"; 14 | internal const string MamlCommandNameTag = "command:name"; 15 | internal const string MamlDescriptionTag = "maml:description"; 16 | internal const string MamlParaTag = "maml:para"; 17 | internal const string MamlSyntaxTag = "command:syntax"; 18 | internal const string MamlSyntaxItemTag = "command:syntaxItem"; 19 | internal const string MamlNameTag = "maml:name"; 20 | internal const string MamlCommandParameterTag = "command:parameter"; 21 | internal const string MamlCommandParameterValueTag = "command:parameterValue"; 22 | internal const string MamlDevTypeTag = "dev:type"; 23 | internal const string MamlDevDefaultValueTag = "dev:defaultValue"; 24 | internal const string MamlCommandParameterValueGroupTag = "command:parameterValueGroup"; 25 | internal const string MamlCommandParametersTag = "command:parameters"; 26 | internal const string MamlCommandInputTypesTag = "command:inputTypes"; 27 | internal const string MamlCommandInputTypeTag = "command:inputType"; 28 | internal const string MamlCommandReturnValuesTag = "command:returnValues"; 29 | internal const string MamlCommandReturnValueTag = "command:returnValue"; 30 | internal const string MamlAlertSetTag = "maml:alertSet"; 31 | internal const string MamlAlertTag = "maml:alert"; 32 | internal const string MamlCommandExamplesTag = "command:examples"; 33 | internal const string MamlCommandExampleTag = "command:example"; 34 | internal const string MamlTitleTag = "maml:title"; 35 | internal const string MamlDevCodeTag = "dev:code"; 36 | internal const string MamlDevRemarksTag = "dev:remarks"; 37 | internal const string MamlCommandRelatedLinksTag = "command:relatedLinks"; 38 | internal const string MamlNavigationLinkTag = "maml:navigationLink"; 39 | internal const string MamlLinkTextTag = "maml:linkText"; 40 | internal const string MamlUriTag = "maml:uri"; 41 | internal const string MamlFileExtensionSuffix = "-help.xml"; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/YamlWriter/YamlConstants.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | namespace Microsoft.PowerShell.PlatyPS.Model 9 | { 10 | // YAML CONSTANTS 11 | internal static partial class Constants 12 | { 13 | internal const string YamlHeader = "---"; 14 | // This should probably be "schema: {0}" 15 | internal const string SchemaVersionYaml = "PlatyPS schema version: 2024-05-01"; 16 | 17 | internal const string YamlExtension = "yml"; 18 | internal const string Example1 = "Example 1"; 19 | internal const string ModuleNameHeaderTemplate = "Module Name: {0}"; 20 | internal const string DownloadHelpLinkTitle = "HelpInfoUri: "; 21 | internal const string HelpVersionTitle = "Help Version: "; 22 | internal const string LocaleTemplate = "Locale: {0}"; 23 | internal const string ModuleGuidHeaderTemplate = "Module Guid: {0}"; 24 | internal const string SynopsisYamlHeader = "synopsis:"; 25 | internal const string SyntaxYamlHeader = "syntaxes:"; 26 | internal const string DescriptionYamlHeader = "description:"; 27 | internal const string ExamplesYamlHeader = "examples:"; 28 | internal const string ParametersYamlHeader = "parameters:"; 29 | internal const string CommonParametersYamlHeader = "- name: CommonParameters"; 30 | internal const string OutputsYamlHeader = "outputs:"; 31 | internal const string InputsYamlHeader = "inputs:"; 32 | internal const string NotesYamlHeader = "notes:"; 33 | internal const string RelatedLinksYamlHeader = "links:"; 34 | internal const string SyntaxYamlTemplate = "- >-"; 35 | internal const string DefaultSyntaxYamlTemplate = "- >-"; 36 | internal const string RelatedLinksYamlFmt = "placeholder"; 37 | internal const string yamlExampleItemHeaderTemplate = "placeholder"; 38 | internal const string yamlNotesItemHeaderTemplate = "placeholder"; 39 | internal const string yamlModulePageModuleNameHeaderTemplate = "placeholder"; 40 | internal const string yamlModulePageDescriptionHeader = "placeholder"; 41 | internal const string yamlModulePageCmdletLinkTemplate = "placeholder"; 42 | internal const string yamlAliasHeader = "aliases:"; 43 | internal const string yamlParameterYamlBlockWithAcceptedValues = "placeholder -{0}"; 44 | } 45 | } 46 | 47 | 48 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Microsoft.PowerShell.PlatyPS 2 | 3 | This project follow the [Microsoft Open Source Code of Conduct][02]. By participating in this 4 | project, you agree to abide by its terms. 5 | 6 | We welcome your contributions and suggestions. 7 | 8 | ## Contributor License Agreement (CLA) 9 | 10 | Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you 11 | have the right to, and actually do, grant us the rights to use your contribution. For more 12 | information, see the [Microsoft CLA][03]. 13 | 14 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 15 | a CLA. If so, the bot will guide you through the process of signing the CLA. 16 | 17 | ## Get started 18 | 19 | There are several ways to contribute to this project: 20 | 21 | - **Report issues**: If you find a bug, please open an issue using the appropriate 22 | [issue template][06]. 23 | - **Suggest features**: If you have an idea for a new feature or improvement, please start a 24 | discussion in the [GitHub discussions][05]. When a feature request is approved, a maintainer will 25 | convert the discussion into an issue that can be the basis for a pull request. 26 | - **Submit pull requests**: If you want to contribute code, please fork the repository and submit a 27 | pull request. Pull requests should be based on the `main` branch and include a clear description 28 | of the changes you are proposing. **Pull requests must be linked to an issue to be considered for 29 | merging.** 30 | - **Contribute to documentation**: There are two kinds of documentation: 31 | - **User documentation**: This is the documentation that provide cmdlet reference and usage 32 | information. This documentation is published to [Microsoft Learn][07] and maintained in the 33 | [MicrosoftDocs/PowerShell-Docs-Modules][04] repository. 34 | - **Developer documentation**: This is the documentation aimed at contributors to the PlatyPS 35 | project. It is stored in the `docs` folder of this repository. For more information, see the 36 | [README][01] file in the `docs` folder. 37 | 38 | 39 | [01]: ./docs/README.md 40 | [02]: CODE_OF_CONDUCT.md 41 | [03]: https://cla.microsoft.com 42 | [04]: https://github.com/MicrosoftDocs/PowerShell-Docs-Modules 43 | [05]: https://github.com/PowerShell/platyPS/discussions 44 | [06]: https://github.com/PowerShell/platyPS/issues/new 45 | [07]: https://learn.microsoft.com/powershell/module/microsoft.powershell.platyps/ 46 | -------------------------------------------------------------------------------- /src/Command/ImportModuleFileCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Management.Automation; 7 | using Microsoft.PowerShell.PlatyPS.Model; 8 | 9 | namespace Microsoft.PowerShell.PlatyPS 10 | { 11 | /// 12 | /// Cmdlet to import a markdown file and convert it to a ModuleFile object. 13 | /// 14 | [Cmdlet(VerbsData.Import, "MarkdownModuleFile", DefaultParameterSetName = "Path", HelpUri = "")] 15 | [OutputType(typeof(ModuleFileInfo))] 16 | public sealed class ImportMarkdownModuleFileCommand : PSCmdlet 17 | { 18 | #region cmdlet parameters 19 | 20 | [Parameter(Mandatory=true, Position=0, ValueFromPipeline=true, ParameterSetName= "Path")] 21 | [ValidateNotNullOrEmpty] 22 | [SupportsWildcards] 23 | public string[] Path { get; set; } = Array.Empty(); 24 | 25 | [Parameter(Mandatory=true, ValueFromPipeline=true, ParameterSetName= "LiteralPath")] 26 | [Alias("PSPath", "LP")] 27 | [ValidateNotNullOrEmpty] 28 | public string[] LiteralPath { get; set; } = Array.Empty(); 29 | 30 | #endregion 31 | 32 | protected override void ProcessRecord() 33 | { 34 | List resolvedPaths; 35 | try 36 | { 37 | // This is a list because the resolution process can result in multiple paths (in the case of non-literal path). 38 | resolvedPaths = PathUtils.ResolvePath(this, ParameterSetName == "LiteralPath" ? LiteralPath : Path, ParameterSetName == "LiteralPath" ? true : false); 39 | } 40 | catch (Exception e) 41 | { 42 | WriteError(new ErrorRecord(e, "Could not resolve Path", ErrorCategory.InvalidOperation, ParameterSetName == "LiteralPath" ? LiteralPath : Path)); 43 | return; 44 | } 45 | 46 | // These should be resolved paths, whether -LiteralPath was used or not. 47 | foreach (string path in resolvedPaths) 48 | { 49 | try 50 | { 51 | WriteObject(MarkdownConverter.GetModuleFileInfoFromMarkdownFile(path)); 52 | } 53 | catch (Exception e) 54 | { 55 | WriteError(new ErrorRecord(e, "FailedToImportMarkdown", ErrorCategory.InvalidOperation, path)); 56 | } 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Command/Measure-PlatyPSMarkdown.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Management.Automation; 7 | using Microsoft.PowerShell.PlatyPS.Model; 8 | 9 | namespace Microsoft.PowerShell.PlatyPS 10 | { 11 | /// 12 | /// Cmdlet to determine the type of markdown file. 13 | /// 14 | [Cmdlet(VerbsDiagnostic.Measure, "PlatyPSMarkdown", DefaultParameterSetName = "Path", HelpUri = "")] 15 | [OutputType(typeof(MarkdownProbeInfo))] 16 | public sealed class MeasurePlatyPSMarkdown : PSCmdlet 17 | { 18 | #region cmdlet parameters 19 | 20 | [Parameter(Mandatory=true, Position=0, ValueFromPipeline=true, ParameterSetName= "Path")] 21 | [ValidateNotNullOrEmpty] 22 | [SupportsWildcards] 23 | public string[] Path { get; set; } = Array.Empty(); 24 | 25 | [Parameter(Mandatory=true, ValueFromPipeline=true, ParameterSetName= "LiteralPath")] 26 | [Alias("PSPath", "LP")] 27 | [ValidateNotNullOrEmpty] 28 | public string[] LiteralPath { get; set; } = Array.Empty(); 29 | 30 | #endregion 31 | 32 | protected override void ProcessRecord() 33 | { 34 | List resolvedPaths; 35 | try 36 | { 37 | // This is a list because the resolution process can result in multiple paths (in the case of non-literal path). 38 | resolvedPaths = PathUtils.ResolvePath(this, ParameterSetName == "LiteralPath" ? LiteralPath : Path, ParameterSetName == "LiteralPath" ? true : false); 39 | } 40 | catch (Exception e) 41 | { 42 | WriteError(new ErrorRecord(e, "Could not resolve Path", ErrorCategory.InvalidOperation, ParameterSetName == "LiteralPath" ? LiteralPath : Path)); 43 | return; 44 | } 45 | 46 | // These should be resolved paths, whether -LiteralPath was used or not. 47 | foreach (string path in resolvedPaths) 48 | { 49 | try 50 | { 51 | var markdownInfo = MarkdownProbe.Identify(path); 52 | WriteObject(markdownInfo); 53 | } 54 | catch (Exception e) 55 | { 56 | WriteError(new ErrorRecord(e, "FailedToImportMarkdown", ErrorCategory.InvalidOperation, path)); 57 | } 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Command/TestMarkdownCommandHelp.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Management.Automation; 7 | 8 | namespace Microsoft.PowerShell.PlatyPS 9 | { 10 | [Cmdlet(VerbsDiagnostic.Test, "MarkdownCommandHelp")] 11 | [OutputType(typeof(bool))] 12 | [OutputType(typeof(MarkdownCommandHelpValidationResult))] 13 | public class TestMarkdownCommandHelpCommand : PSCmdlet 14 | { 15 | /// 16 | /// Gets or Sets the path to the item. 17 | /// 18 | [Parameter(Position = 0, ParameterSetName = "Item", Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] 19 | [SupportsWildcards] 20 | public string[] Path { get; set; } = Array.Empty(); 21 | 22 | /// 23 | /// Gets or Sets the literal path to the item. 24 | /// 25 | [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "Literal")] 26 | [Alias("PSPath", "LP")] 27 | public string[] LiteralPath { get; set; } = Array.Empty(); 28 | 29 | [Parameter] 30 | public SwitchParameter DetailView { get; set; } 31 | 32 | protected override void ProcessRecord() 33 | { 34 | List resolvedPaths; 35 | try 36 | { 37 | // This is a list because the resolution process can result in multiple paths (in the case of non-literal path). 38 | resolvedPaths = PathUtils.ResolvePath(this, (ParameterSetName == "LiteralPath" ? LiteralPath : Path), ParameterSetName == "LiteralPath" ? true : false); 39 | } 40 | catch (Exception e) 41 | { 42 | WriteError(new ErrorRecord(e, "Could not resolve Path", ErrorCategory.InvalidOperation, ParameterSetName == "LiteralPath" ? LiteralPath : Path)); 43 | return; 44 | } 45 | 46 | foreach (var resolvedPath in resolvedPaths) 47 | { 48 | var result = MarkdownConverter.ValidateMarkdownFile(resolvedPath, out var Detail); 49 | if (DetailView) 50 | { 51 | WriteObject(new MarkdownCommandHelpValidationResult(resolvedPath, result, Detail)); 52 | } 53 | else 54 | { 55 | WriteObject(result); 56 | } 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Command/ImportMamlCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.ObjectModel; 6 | using System.Management.Automation; 7 | using Microsoft.PowerShell.PlatyPS.Model; 8 | 9 | namespace Microsoft.PowerShell.PlatyPS 10 | { 11 | /// 12 | /// Cmdlet to generate the markdown help for commands, all commands in a module or from a MAML file. 13 | /// 14 | [Cmdlet(VerbsData.Import, "MamlHelp", HelpUri = "", DefaultParameterSetName = "Path")] 15 | [OutputType(typeof(CommandHelp[]))] 16 | public sealed class ImportMamlHelpCommand : PSCmdlet 17 | { 18 | #region cmdlet parameters 19 | 20 | [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true, ParameterSetName = "Path")] 21 | [SupportsWildcards] 22 | [ValidateNotNullOrEmpty] 23 | public string[] Path { get; set; } = new string[0]; 24 | 25 | [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "LiteralPath")] 26 | [Alias("PSPath", "LP")] 27 | [ValidateNotNullOrEmpty] 28 | public string[] LiteralPath { get; set; } = new string[0]; 29 | 30 | TransformMaml? transformer; 31 | 32 | protected override void BeginProcessing () 33 | { 34 | transformer = new TransformMaml(new TransformSettings()); 35 | } 36 | 37 | #endregion 38 | 39 | protected override void ProcessRecord() 40 | { 41 | Collection? cmdHelpObjs = null; 42 | 43 | bool isLiteralPath = string.Compare(ParameterSetName, "LiteralPath") == 0; 44 | var paths = isLiteralPath ? LiteralPath : Path; 45 | foreach(var mamlPath in PathUtils.ResolvePath(this, paths, isLiteralPath)) 46 | { 47 | try 48 | { 49 | cmdHelpObjs = transformer?.Transform(new string[] { mamlPath }); 50 | 51 | if (cmdHelpObjs != null) 52 | { 53 | foreach (var cmdletHelp in cmdHelpObjs) 54 | { 55 | WriteObject(cmdletHelp); 56 | } 57 | } 58 | } 59 | catch (Exception e) 60 | { 61 | WriteError(new ErrorRecord(e, "FailedToTransformMaml", ErrorCategory.InvalidOperation, mamlPath)); 62 | } 63 | } 64 | } 65 | } 66 | } 67 | 68 | -------------------------------------------------------------------------------- /test/Pester/DiagnosticMessage.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | Describe "Test Diagnostic Messages" { 5 | BeforeAll { 6 | $filePath = [io.path]::Combine($PSScriptRoot, "assets", "get-date.md") 7 | $ch = Import-MarkdownCommandHelp $filePath 8 | } 9 | 10 | It "The CommandHelp object should have diagnostic messages" { 11 | $ch.Diagnostics | Should -Not -BeNullOrEmpty 12 | } 13 | 14 | It "Should properly report on the state of the parse" { 15 | $ch.Diagnostics.HadErrors | Should -Be $False 16 | } 17 | 18 | It "Should properly report the path to the file" { 19 | $ch.Diagnostics.FileName | Should -Be $filePath 20 | } 21 | 22 | It "Should populate the Messages property" { 23 | $ch.Diagnostics.Messages.Count | Should -Be 37 24 | } 25 | 26 | It "Should have the correct number of Information entries" { 27 | $ch.Diagnostics.Messages.Where({$_.Severity -eq "Information"}).Count | Should -Be 36 28 | } 29 | 30 | # this is 13 parameter names and the initial parameter finding which reports the number 31 | # plus the common parameters 32 | It "Should find the correct number of parameters" { 33 | $ch.Diagnostics.Messages.Where({$_.Source -eq "Parameter"}).Count | Should be 15 34 | } 35 | 36 | It "Should find the common parameters" { 37 | $ch.Diagnostics.Messages.Where({$_.Message -match "CommonParameters"}).Count | Should -Be 1 38 | } 39 | 40 | It "Should find other parameter names" { 41 | $parameterNames = "AsUTC", "Date", "Day", "DisplayHint", "Format", "Hour", 42 | "Millisecond", "Minute", "Month", "Second", "UFormat", "UnixTimeSeconds", "Year" 43 | $pattern = $parameterNames -join "|" 44 | $ch.Diagnostics.Messages.Where({$_.Message -match $pattern}).Count | Should -Be 13 45 | } 46 | 47 | Context "Error Conditions" { 48 | BeforeAll { 49 | $errorPath = [io.path]::Combine($PSScriptRoot, "assets", "get-datebad.md") 50 | $errorCh = Import-MarkdownCommandHelp $errorPath 51 | } 52 | 53 | It "Should report on the missing metadata" { 54 | $errorCh.Diagnostics.Messages.Where({$_.Source -eq "Metadata" -and $_.Identifier -match "schema"}).Severity | Should -Be "Error" 55 | } 56 | 57 | It "Should report on the missing Synopsis" { 58 | $errorCh.Diagnostics.Messages.Where({$_.Source -eq "Synopsis"}).Severity | Should -Be "Error" 59 | 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Command/ImportMarkdownCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Management.Automation; 7 | using Microsoft.PowerShell.PlatyPS.Model; 8 | 9 | namespace Microsoft.PowerShell.PlatyPS 10 | { 11 | /// 12 | /// Cmdlet to import a markdown file and convert it to a CommandHelp object. 13 | /// 14 | [Cmdlet(VerbsData.Import, "MarkdownCommandHelp", DefaultParameterSetName = "Path", HelpUri = "")] 15 | [OutputType(typeof(CommandHelp))] 16 | public sealed class ImportMarkdownHelpCommand : PSCmdlet 17 | { 18 | #region cmdlet parameters 19 | 20 | [Parameter(Mandatory=true, Position=0, ValueFromPipeline=true, ParameterSetName= "Path")] 21 | [ValidateNotNullOrEmpty] 22 | [SupportsWildcards] 23 | public string[] Path { get; set; } = Array.Empty(); 24 | 25 | [Parameter(Mandatory=true, ValueFromPipeline=true, ParameterSetName= "LiteralPath")] 26 | [Alias("PSPath", "LP")] 27 | [ValidateNotNullOrEmpty] 28 | public string[] LiteralPath { get; set; } = Array.Empty(); 29 | 30 | #endregion 31 | 32 | protected override void ProcessRecord() 33 | { 34 | List resolvedPaths; 35 | try 36 | { 37 | // This is a list because the resolution process can result in multiple paths (in the case of non-literal path). 38 | resolvedPaths = PathUtils.ResolvePath(this, ParameterSetName == "LiteralPath" ? LiteralPath : Path, ParameterSetName == "LiteralPath" ? true : false); 39 | } 40 | catch (Exception e) 41 | { 42 | WriteError(new ErrorRecord(e, "Could not resolve Path", ErrorCategory.InvalidOperation, ParameterSetName == "LiteralPath" ? LiteralPath : Path)); 43 | return; 44 | } 45 | 46 | // These should be resolved paths, whether -LiteralPath was used or not. 47 | foreach (string path in resolvedPaths) 48 | { 49 | try 50 | { 51 | var commandHelpObject = MarkdownConverter.GetCommandHelpFromMarkdownFile(path); 52 | WriteObject(commandHelpObject); 53 | } 54 | catch (Exception e) 55 | { 56 | WriteError(new ErrorRecord(e, "FailedToImportMarkdown", ErrorCategory.InvalidOperation, path)); 57 | } 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Model/Links.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Linq; 6 | using Markdig.Extensions.AutoLinks; 7 | 8 | namespace Microsoft.PowerShell.PlatyPS.Model 9 | { 10 | /// 11 | /// Class to represent the properties of a link in PowerShell help. 12 | /// 13 | public class Links : IEquatable 14 | { 15 | public string Uri { get; set;} 16 | public string LinkText { get; set;} 17 | 18 | public Links(string uri, string linkText) 19 | { 20 | Uri = uri; 21 | LinkText = linkText; 22 | } 23 | 24 | /// 25 | /// Create a new Links object from an existing one. 26 | /// 27 | /// The link to copy. 28 | public Links (Links link) 29 | { 30 | Uri = link.Uri; 31 | LinkText = link.LinkText; 32 | } 33 | 34 | internal string ToRelatedLinksString(string fmt) 35 | { 36 | // return $"[{LinkText}]({Uri})"; 37 | return string.Format(fmt, LinkText, Uri); 38 | } 39 | 40 | public bool Equals(Links other) 41 | { 42 | if (other is null) 43 | { 44 | return false; 45 | } 46 | 47 | return (string.Compare(Uri, other.Uri, StringComparison.CurrentCulture) == 0 && string.Compare(LinkText, other.LinkText, StringComparison.CurrentCulture) == 0); 48 | } 49 | 50 | public override bool Equals(object other) 51 | { 52 | if (other is null) 53 | { 54 | return false; 55 | } 56 | 57 | if (other is Links link2) 58 | { 59 | return Equals(link2); 60 | } 61 | 62 | return false; 63 | } 64 | 65 | public override int GetHashCode() 66 | { 67 | return (Uri, LinkText).GetHashCode(); 68 | } 69 | 70 | public static bool operator == (Links link1, Links link2) 71 | { 72 | if (link1 is not null && link2 is not null) 73 | { 74 | return link1.Equals(link2); 75 | } 76 | 77 | return false; 78 | } 79 | 80 | public static bool operator != (Links link1, Links link2) 81 | { 82 | if (link1 is not null && link2 is not null) 83 | { 84 | return ! link1.Equals(link2); 85 | } 86 | 87 | return false; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /test/Pester/ImportMamlHelp.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | Describe "Import-YamlHelp tests" { 5 | Context "Basic tests" { 6 | BeforeAll { 7 | $mamlFile = "${PSScriptRoot}/assets/Microsoft.PowerShell.Commands.Utility.dll-Help.xml" 8 | $importedCmds = Import-MamlHelp $mamlFile 9 | $cmdlet = $importedCmds.Where({$_.title -eq "add-member"}) 10 | } 11 | 12 | Context "General tests" { 13 | It "Can import a MAML file with the proper number of commands" { 14 | $expectedCount = (Select-String "" $mamlFile | foreach-object { $_.Line -replace ".*>(.*)<.*",'$1' } 20 | $importedCmds.title | Should -Be $expectedNames 21 | } 22 | 23 | It "Identifies the command which does not have CmdletBinding" { 24 | $importedCmds.Where({!$_.HasCmdletBinding}) | Should -Be "Show-Command" 25 | } 26 | 27 | It "Correctly retrieves the synopsis for Add-Member" { 28 | # this is the string from the MAML file. 29 | $expected = "Adds custom properties and methods to an instance of a PowerShell object." 30 | $importedCmds.Where({$_.title -eq "Add-Member"}).Synopsis | Should -Be $expected 31 | } 32 | } 33 | 34 | Context "Metadata checks" { 35 | It "has the proper metadata value for '' for the Add-Member cmdlet" -testcases @( 36 | @{ Key = 'document type'; Value = 'cmdlet' } 37 | @{ Key = 'title'; Value = 'Add-Member' } 38 | @{ Key = 'Module Name'; Value = 'Microsoft.PowerShell.Commands.Utility' } 39 | @{ Key = 'Locale'; Value = 'en-US' } 40 | @{ Key = 'PlatyPS schema version'; Value = '2024-05-01' } 41 | @{ Key = 'HelpUri'; Value = 'https://learn.microsoft.com/powershell/module/microsoft.powershell.utility/add-member?view=powershell-7.3&WT.mc_id=ps-gethelp' } 42 | @{ Key = 'ms.date'; Value = Get-Date -f "MM/dd/yyyy" } 43 | @{ Key = 'external help file'; Value = 'Microsoft.PowerShell.Commands.Utility.dll-Help.xml' } 44 | ) { 45 | param ([string]$Key, [string]$Value) 46 | $cmdlet.Metadata[$key] | Should -Be $Value 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/Common/OrderedSet.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | using System.Collections.Specialized; 8 | 9 | namespace Microsoft.PowerShell.PlatyPS 10 | { 11 | /// 12 | /// A set that maintains the order of items as they are added. 13 | /// 14 | /// The type 15 | public class OrderedSet : ICollection 16 | { 17 | private readonly IDictionary> m_Dictionary; 18 | private readonly LinkedList m_LinkedList; 19 | 20 | public OrderedSet() 21 | : this(EqualityComparer.Default) 22 | { 23 | } 24 | 25 | public OrderedSet(IEqualityComparer comparer) 26 | { 27 | m_Dictionary = new Dictionary>(comparer); 28 | m_LinkedList = new LinkedList(); 29 | } 30 | 31 | public int Count 32 | { 33 | get { return m_Dictionary.Count; } 34 | } 35 | 36 | public virtual bool IsReadOnly 37 | { 38 | get { return m_Dictionary.IsReadOnly; } 39 | } 40 | 41 | void ICollection.Add(T item) 42 | { 43 | Add(item); 44 | } 45 | 46 | public void Clear() 47 | { 48 | m_LinkedList.Clear(); 49 | m_Dictionary.Clear(); 50 | } 51 | 52 | public bool Remove(T item) 53 | { 54 | LinkedListNode node; 55 | bool found = m_Dictionary.TryGetValue(item, out node); 56 | if (!found) return false; 57 | m_Dictionary.Remove(item); 58 | m_LinkedList.Remove(node); 59 | return true; 60 | } 61 | 62 | public IEnumerator GetEnumerator() 63 | { 64 | return m_LinkedList.GetEnumerator(); 65 | } 66 | 67 | IEnumerator IEnumerable.GetEnumerator() 68 | { 69 | return GetEnumerator(); 70 | } 71 | 72 | public bool Contains(T item) 73 | { 74 | return m_Dictionary.ContainsKey(item); 75 | } 76 | 77 | public void CopyTo(T[] array, int arrayIndex) 78 | { 79 | m_LinkedList.CopyTo(array, arrayIndex); 80 | } 81 | 82 | public bool Add(T item) 83 | { 84 | if (m_Dictionary.ContainsKey(item)) return false; 85 | LinkedListNode node = m_LinkedList.AddLast(item); 86 | m_Dictionary.Add(item, node); 87 | return true; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/MarkdownReader/ModuleCommandGroup.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | 8 | namespace Microsoft.PowerShell.PlatyPS 9 | { 10 | public class ModuleCommandGroup : IEquatable 11 | { 12 | public string GroupTitle { get; set; } 13 | public List Commands { get; set; } 14 | 15 | public ModuleCommandGroup() 16 | { 17 | GroupTitle = string.Empty; 18 | Commands = new(); 19 | } 20 | 21 | public ModuleCommandGroup(ModuleCommandGroup mcg) 22 | { 23 | GroupTitle = mcg.GroupTitle; 24 | Commands = new(); 25 | foreach(var command in mcg.Commands) 26 | { 27 | Commands.Add(new ModuleCommandInfo(command)); 28 | } 29 | } 30 | 31 | public ModuleCommandGroup(string title) 32 | { 33 | GroupTitle = title; 34 | Commands = new(); 35 | } 36 | 37 | public override int GetHashCode() 38 | { 39 | return (GroupTitle, Commands).GetHashCode(); 40 | } 41 | 42 | public bool Equals(ModuleCommandGroup other) 43 | { 44 | if (other is null) 45 | { 46 | return false; 47 | } 48 | 49 | return ( 50 | string.Compare(GroupTitle, other.GroupTitle, StringComparison.CurrentCultureIgnoreCase) == 0 && 51 | Commands.SequenceEqual(other.Commands) 52 | ); 53 | } 54 | 55 | public override bool Equals(object other) 56 | { 57 | if (other is null) 58 | { 59 | return false; 60 | } 61 | 62 | if (other is ModuleCommandGroup group2) 63 | { 64 | return Equals(group2); 65 | } 66 | 67 | return false; 68 | } 69 | 70 | public static bool operator == (ModuleCommandGroup group1, ModuleCommandGroup group2) 71 | { 72 | if (group1 is not null && group2 is not null) 73 | { 74 | return group1.Equals(group2); 75 | } 76 | 77 | return false; 78 | } 79 | 80 | public static bool operator !=(ModuleCommandGroup group1, ModuleCommandGroup group2) 81 | { 82 | if (group1 is not null && group2 is not null) 83 | { 84 | return ! group1.Equals(group2); 85 | } 86 | 87 | return false; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /test/Pester/Model.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | Describe "Model type tests" { 5 | BeforeAll { 6 | $cmdInfo = get-command New-MarkdownCommandHelp 7 | $commandHelpType = $cmdInfo.ImplementingType.Assembly.GetType("Microsoft.PowerShell.PlatyPS.Model.CommandHelp") 8 | $ObjectProperties = @( 9 | @{ type = 'System.Globalization.CultureInfo'; Nullable = $false; Name = "Locale" } 10 | @{ type = 'System.Nullable`1[System.Guid]'; Nullable = $true; Name = "ModuleGuid" } 11 | @{ type = 'System.String'; Nullable = $true; Name = "ExternalHelpFile" } 12 | @{ type = 'System.String'; Nullable = $true; Name = "OnlineVersionUrl" } 13 | @{ type = 'System.String'; Nullable = $true; Name = "SchemaVersion" } 14 | @{ type = 'System.String'; Nullable = $false; Name = "ModuleName" } 15 | @{ type = 'System.String'; Nullable = $false; Name = "Title" } 16 | @{ type = 'System.String'; Nullable = $false; Name = "Synopsis" } 17 | @{ type = 'System.Collections.Generic.List`1[Microsoft.PowerShell.PlatyPS.Model.SyntaxItem]'; Nullable = $false; Name = "Syntax" } 18 | @{ type = 'System.String'; Nullable = $false; Name = "Aliases" } 19 | @{ type = 'System.String'; Nullable = $true; Name = "Description" } 20 | @{ type = 'System.Collections.Generic.List`1[Microsoft.PowerShell.PlatyPS.Model.Example]'; Nullable = $true; Name = "Examples" } 21 | @{ type = 'System.Collections.Generic.List`1[Microsoft.PowerShell.PlatyPS.Model.Parameter]'; Nullable = $false; Name = "Parameters" } 22 | @{ type = 'System.Collections.Generic.List`1[Microsoft.PowerShell.PlatyPS.Model.InputOutput]'; Nullable = $true; Name = "Inputs" } 23 | @{ type = 'System.Collections.Generic.List`1[Microsoft.PowerShell.PlatyPS.Model.InputOutput]'; Nullable = $true; Name = "Outputs" } 24 | @{ type = 'System.Collections.Generic.List`1[Microsoft.PowerShell.PlatyPS.Model.Links]'; Nullable = $true; Name = "RelatedLinks" } 25 | @{ type = 'System.Boolean'; Name = "HasCmdletBinding" } 26 | @{ type = 'System.String'; Nullable = $true; Name = "Notes" } 27 | ) 28 | $BindingFlags = [System.Reflection.BindingFlags]"Instance,NonPublic,Public" 29 | } 30 | 31 | It "CommandHelp has the correct type information for ''" -TestCases $ObjectProperties { 32 | param ( $name, $type, $nullable) 33 | $property = $commandHelpType.GetProperty($name, $BindingFlags) 34 | $property.PropertyType.ToString() | Should -BeExactly $type 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.ci/test.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | jobName: TestPkgWin 3 | imageName: windows-latest 4 | displayName: PowerShell Core on Windows 5 | powershellExecutable: pwsh 6 | 7 | jobs: 8 | - job: ${{ parameters.jobName }} 9 | pool: 10 | vmImage: ${{ parameters.imageName }} 11 | displayName: ${{ parameters.displayName }} 12 | steps: 13 | - ${{ parameters.powershellExecutable }}: | 14 | Get-InstalledModule -Name pester -AllowPrerelease -MinimumVersion 5.0.0 -ErrorAction SilentlyContinue | Uninstall-Module -Force -Verbose -ErrorAction SilentlyContinue 15 | displayName: Remove >= 5.0.0 Pester 16 | timeoutInMinutes: 10 17 | 18 | - ${{ parameters.powershellExecutable }}: | 19 | Install-Module Pester -Force -MaximumVersion 4.99 -SkipPublisherCheck 20 | displayName: Install dependencies - Pester 21 | timeoutInMinutes: 10 22 | 23 | - task: DownloadBuildArtifacts@0 24 | displayName: 'Download artifacts' 25 | inputs: 26 | buildType: current 27 | downloadType: specific 28 | itemPattern: '**/*.nupkg' 29 | downloadPath: '$(System.ArtifactsDirectory)' 30 | artifactName: 'nupkg' 31 | 32 | - ${{ parameters.powershellExecutable }}: | 33 | $sourceName = 'pspackageproject-local-repo' 34 | Register-PSRepository -Name $sourceName -SourceLocation '$(System.ArtifactsDirectory)/nupkg' -ErrorAction Ignore -Verbose 35 | $null = New-Item -Path '$(System.ArtifactsDirectory)/saved' -ItemType Directory 36 | Save-Module -Repository $sourceName -Name Microsoft.PowerShell.PlatyPS -Path '$(System.ArtifactsDirectory)/saved' -Verbose 37 | displayName: Extract product artifact 38 | timeoutInMinutes: 10 39 | 40 | - ${{ parameters.powershellExecutable }}: | 41 | $(Build.SourcesDirectory)/build.ps1 -OutputDir '$(System.ArtifactsDirectory)/saved' -Test 42 | 43 | $pesterResult = '$(Build.SourcesDirectory)/pester.tests.xml' 44 | 45 | if (-not (Test-Path $pesterResult)) 46 | { 47 | throw 'Pester test results not found' 48 | } 49 | 50 | Write-Host "##vso[results.publish type=NUnit;mergeResults=true;runTitle=Pester;publishRunAttachments=true;resultFiles=$pesterResult;]" 51 | displayName: Execute functional tests 52 | errorActionPreference: continue 53 | 54 | - task: PublishTestResults@2 55 | inputs: 56 | testResultsFormat: 'NUnit' 57 | testResultsFiles: '**/pester.tests.xml' 58 | testRunTitle: '${{ parameters.jobName }}-Pester' 59 | 60 | - ${{ parameters.powershellExecutable }}: | 61 | Unregister-PSRepository -Name 'pspackageproject-local-repo' -ErrorAction Ignore 62 | displayName: Unregister temporary PSRepository 63 | condition: always() 64 | timeoutInMinutes: 10 65 | -------------------------------------------------------------------------------- /test/Pester/assets/Microsoft.PowerShell.Security.md: -------------------------------------------------------------------------------- 1 | --- 2 | Download Help Link: https://aka.ms/powershell75-help 3 | Help Version: 7.5.0.0 4 | Locale: en-US 5 | Module Guid: a94c8c7e-9810-47c0-b8af-65089c13a35a 6 | Module Name: Microsoft.PowerShell.Security 7 | ms.date: 06/09/2017 8 | schema: 2.0.0 9 | title: Microsoft.PowerShell.Security 10 | --- 11 | # Microsoft.PowerShell.Security Module 12 | 13 | ## Description 14 | 15 | This section contains the help topics for the cmdlets that are installed with PowerShell 16 | Microsoft.PowerShell.Security module. The Security module contains cmdlets and providers that manage 17 | the basic security features of Windows. 18 | 19 | ## Microsoft.PowerShell.Security Cmdlets 20 | 21 | ### [ConvertFrom-SecureString](ConvertFrom-SecureString.md) 22 | Converts a secure string to an encrypted standard string. 23 | 24 | ### [ConvertTo-SecureString](ConvertTo-SecureString.md) 25 | Converts encrypted standard strings to secure strings. 26 | 27 | ### [Get-Acl](Get-Acl.md) 28 | Gets the security descriptor for a resource, such as a file or registry key. 29 | 30 | ### [Get-AuthenticodeSignature](Get-AuthenticodeSignature.md) 31 | Gets information about the Authenticode signature for a file. 32 | 33 | ### [Get-CmsMessage](Get-CmsMessage.md) 34 | Gets content that has been encrypted by using the Cryptographic Message Syntax format. 35 | 36 | ### [Get-Credential](Get-Credential.md) 37 | Gets a credential object based on a user name and password. 38 | 39 | ### [Get-ExecutionPolicy](Get-ExecutionPolicy.md) 40 | Gets the execution policies for the current session. 41 | 42 | ### [Get-PfxCertificate](Get-PfxCertificate.md) 43 | Gets information about PFX certificate files on the computer. 44 | 45 | ### [New-FileCatalog](New-FileCatalog.md) 46 | Creates a Windows catalog file containing cryptographic hashes for files and folders in specified 47 | paths. 48 | 49 | ### [Protect-CmsMessage](Protect-CmsMessage.md) 50 | Encrypts content by using the Cryptographic Message Syntax format. 51 | 52 | ### [Set-Acl](Set-Acl.md) 53 | Changes the security descriptor of a specified item, such as a file or a registry key. 54 | 55 | ### [Set-AuthenticodeSignature](Set-AuthenticodeSignature.md) 56 | Adds an Authenticode signature to a PowerShell script or other file. 57 | 58 | ### [Set-ExecutionPolicy](Set-ExecutionPolicy.md) 59 | Sets the PowerShell execution policies for Windows computers. 60 | 61 | ### [Test-FileCatalog](Test-FileCatalog.md) 62 | Validates whether the hashes contained in a catalog file (.cat) matches the hashes of the actual files in order to validate their authenticity. 63 | 64 | ### [Unprotect-CmsMessage](Unprotect-CmsMessage.md) 65 | Decrypts content that has been encrypted by using the Cryptographic Message Syntax format. 66 | 67 | -------------------------------------------------------------------------------- /test/Pester/assets/Compare-CommandHelp2.md: -------------------------------------------------------------------------------- 1 | --- 2 | document type: cmdlet 3 | external help file: Microsoft.PowerShell.PlatyPS.dll-Help.xml 4 | HelpUri: '' 5 | Locale: en-US 6 | Module Name: Microsoft.PowerShell.PlatyPS 7 | ms.date: 03/07/2025 8 | PlatyPS schema version: 2024-05-01 9 | title: Compare-CommandHelp 10 | --- 11 | 12 | # Compare-CommandHelp 13 | 14 | ## SYNOPSIS 15 | 16 | ## SYNTAX 17 | 18 | ### __AllParameterSets 19 | 20 | ``` 21 | Compare-CommandHelp [-ReferenceCommandHelp] [-DifferenceCommandHelp] 22 | [-PropertyNamesToExclude ] [] 23 | ``` 24 | 25 | ## ALIASES 26 | 27 | This cmdlet has the following aliases, 28 | 29 | ## DESCRIPTION 30 | 31 | ## EXAMPLES 32 | 33 | ### Example 1 34 | 35 | ## PARAMETERS 36 | 37 | ### -DifferenceCommandHelp 38 | 39 | ```yaml 40 | Type: Microsoft.PowerShell.PlatyPS.Model.CommandHelp 41 | DefaultValue: '' 42 | SupportsWildcards: false 43 | ParameterValue: [] 44 | Aliases: [] 45 | ParameterSets: 46 | - Name: (All) 47 | Position: 1 48 | IsRequired: true 49 | ValueFromPipeline: false 50 | ValueFromPipelineByPropertyName: true 51 | ValueFromRemainingArguments: false 52 | DontShow: false 53 | AcceptedValues: [] 54 | HelpMessage: '' 55 | ``` 56 | 57 | ### -PropertyNamesToExclude 58 | 59 | ```yaml 60 | Type: System.String[] 61 | DefaultValue: '' 62 | SupportsWildcards: false 63 | ParameterValue: [] 64 | Aliases: [] 65 | ParameterSets: 66 | - Name: (All) 67 | Position: Named 68 | IsRequired: false 69 | ValueFromPipeline: false 70 | ValueFromPipelineByPropertyName: false 71 | ValueFromRemainingArguments: false 72 | DontShow: false 73 | AcceptedValues: [] 74 | HelpMessage: '' 75 | ``` 76 | 77 | ### -ReferenceCommandHelp 78 | 79 | ```yaml 80 | Type: Microsoft.PowerShell.PlatyPS.Model.CommandHelp 81 | DefaultValue: '' 82 | SupportsWildcards: false 83 | ParameterValue: [] 84 | Aliases: [] 85 | ParameterSets: 86 | - Name: (All) 87 | Position: 0 88 | IsRequired: true 89 | ValueFromPipeline: false 90 | ValueFromPipelineByPropertyName: true 91 | ValueFromRemainingArguments: false 92 | DontShow: false 93 | AcceptedValues: [] 94 | HelpMessage: '' 95 | ``` 96 | 97 | ### CommonParameters 98 | 99 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, 100 | -InformationAction, -InformationVariable, -OutBuffer, -OutVariable, -PipelineVariable, 101 | -ProgressAction, -Verbose, -WarningAction, and -WarningVariable. For more information, see 102 | [about_CommonParameters](https://go.microsoft.com/fwlink/?LinkID=113216). 103 | 104 | ## INPUTS 105 | 106 | ### Microsoft.PowerShell.PlatyPS.Model.CommandHelp 107 | 108 | ## OUTPUTS 109 | 110 | ### System.String 111 | 112 | ## NOTES 113 | 114 | ## RELATED LINKS 115 | -------------------------------------------------------------------------------- /test/Pester/CompareCommandHelp.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | Describe "Compare-CommandHelp can find differences" { 5 | BeforeAll { 6 | $ch1 = Import-MarkdownCommandHelp "${PSScriptRoot}/assets/get-date.md" 7 | $ch2 = Import-MarkdownCommandHelp "${PSScriptRoot}/assets/get-date.alt01.md" 8 | $result1 = Compare-CommandHelp $ch1 $ch2 -PropertyNamesToExclude @() 9 | $result2 = Compare-CommandHelp $ch1 $ch2 -PropertyNamesToExclude Diagnostics 10 | $result3 = Compare-CommandHelp $ch1 $ch2 11 | } 12 | 13 | It "Should properly identify that the objects are different" { 14 | $result1[-1] | Should -Be "Comparison result: 'NOT OK'" 15 | } 16 | 17 | It "Should properly identify the number of differences" { 18 | $result1.where({$_ -match "are not the same|are different"}).Count | Should -Be 11 19 | } 20 | 21 | It "Should properly identify the elements which are different" { 22 | $expected = "CommandHelp.Syntax.ParameterSetName", "CommandHelp.Syntax.ParameterNames", "CommandHelp.Syntax.ParameterSetName", 23 | "CommandHelp.Syntax.ParameterNames", "CommandHelp.Syntax.ParameterSetName", "CommandHelp.Syntax.ParameterNames", 24 | "CommandHelp.Syntax.ParameterSetName", "CommandHelp.Syntax.ParameterNames", "CommandHelp.Examples.Title", 25 | "CommandHelp.Examples.Remarks", "CommandHelp.Diagnostics" 26 | $observed = $result1.split("`n").Where({$_ -match "are not the same|are different"}).foreach({$_.Substring(2).trim().split()[0]}) 27 | $observed | Should -Be $expected 28 | } 29 | 30 | It "Should be possible to exclude an element from comparison" { 31 | $expected = "CommandHelp.Syntax.ParameterSetName", "CommandHelp.Syntax.ParameterNames", "CommandHelp.Syntax.ParameterSetName", 32 | "CommandHelp.Syntax.ParameterNames", "CommandHelp.Syntax.ParameterSetName", "CommandHelp.Syntax.ParameterNames", 33 | "CommandHelp.Syntax.ParameterSetName", "CommandHelp.Syntax.ParameterNames", "CommandHelp.Examples.Title", 34 | "CommandHelp.Examples.Remarks" 35 | $observed = $result2.split("`n").Where({$_ -match "are not the same|are different"}).foreach({$_.Substring(2).trim().split()[0]}) 36 | $observed | Should -Be $expected 37 | 38 | } 39 | 40 | It "Default excluded properties should be Diagnostics and ParameterNames" { 41 | $expected = "M excluding comparison of CommandHelp.AliasHeaderFound", 42 | "M excluding comparison of CommandHelp.Diagnostics", 43 | "M excluding comparison of CommandHelp.Syntax.ParameterNames" 44 | $observed = $result3.split("`n").Where({$_ -match "excluding comparison"})|Sort-Object -Unique 45 | $observed | Should -Be $expected 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /test/Pester/assets/Out-Null.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: System.Management.Automation.dll-Help.xml 3 | Locale: en-US 4 | Module Name: Microsoft.PowerShell.Core 5 | ms.date: 12/09/2022 6 | online version: https://learn.microsoft.com/powershell/module/microsoft.powershell.core/out-null?view=powershell-7.4&WT.mc_id=ps-gethelp 7 | schema: 2.0.0 8 | title: Out-Null 9 | --- 10 | # Out-Null 11 | 12 | ## SYNOPSIS 13 | Hides the output instead of sending it down the pipeline or displaying it. 14 | 15 | ## SYNTAX 16 | 17 | ``` 18 | Out-Null [-InputObject ] [] 19 | ``` 20 | 21 | ## DESCRIPTION 22 | 23 | The `Out-Null` cmdlet sends its output to NULL, in effect, removing it from the pipeline and 24 | preventing the output from being displayed on screen. 25 | 26 | ## EXAMPLES 27 | 28 | ### Example 1: Delete output 29 | 30 | ```powershell 31 | Get-ChildItem | Out-Null 32 | ``` 33 | 34 | This command gets items in the current location/directory, but its output is not passed through the 35 | pipeline nor displayed at the command line. This is useful for hiding output that you do not need. 36 | 37 | ## PARAMETERS 38 | 39 | ### -InputObject 40 | 41 | Specifies the object to be sent to NULL (removed from pipeline). Enter a variable that contains the 42 | objects, or type a command or expression that gets the objects. 43 | 44 | ```yaml 45 | Type: System.Management.Automation.PSObject 46 | Parameter Sets: (All) 47 | Aliases: 48 | 49 | Required: False 50 | Position: Named 51 | Default value: None 52 | Accept pipeline input: True (ByValue) 53 | Accept wildcard characters: False 54 | ``` 55 | 56 | ### CommonParameters 57 | 58 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, 59 | -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, 60 | -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](https://go.microsoft.com/fwlink/?LinkID=113216). 61 | 62 | ## INPUTS 63 | 64 | ### System.Management.Automation.PSObject 65 | 66 | You can pipe any object to this cmdlet. 67 | 68 | ## OUTPUTS 69 | 70 | ### None 71 | 72 | This cmdlet returns no output. 73 | 74 | ## NOTES 75 | 76 | - The cmdlets that contain the **Out** verb (the **Out** cmdlets) do not have parameters for names 77 | or file paths. To send data to an **Out** cmdlet, use a pipeline operator (`|`) to send the output 78 | of a PowerShell command to the cmdlet. You can also store data in a variable and use the 79 | **InputObject** parameter to pass the data to the cmdlet. For more information, see the examples. 80 | - `Out-Null` does not return any output objects. If you pipe the output of `Out-Null` to the 81 | Get-Member cmdlet, `Get-Member` reports that no objects have been specified. 82 | 83 | ## RELATED LINKS 84 | 85 | [Out-Default](Out-Default.md) 86 | 87 | [Out-Host](Out-Host.md) 88 | -------------------------------------------------------------------------------- /src/MamlWriter/DataType.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Xml.Serialization; 7 | 8 | namespace Microsoft.PowerShell.PlatyPS.MAML 9 | { 10 | /// 11 | /// Represents a "dev:type" element in a Powershell MAML help document. 12 | /// 13 | [XmlRoot("dataType", Namespace = Constants.XmlNamespace.Dev)] 14 | public class DataType 15 | { 16 | /// 17 | /// The data-type name. 18 | /// 19 | [XmlElement("name", Namespace = Constants.XmlNamespace.Dev, Order = 0)] 20 | public string Name { get; set; } = string.Empty; 21 | 22 | /// 23 | /// The data-type URI (unused, for the most part). 24 | /// 25 | // [XmlElement("uri", Namespace = Constants.XmlNamespace.MAML, Order = 1)] 26 | [XmlIgnore] 27 | public string Uri { get; set; } = string.Empty; 28 | 29 | /// 30 | /// The data-type's detailed description (one or more paragraphs; "maml:description/maml:para"). 31 | /// 32 | // [XmlArray("description", Namespace = Constants.XmlNamespace.MAML, Order = 2)] 33 | // [XmlArrayItem("para", Namespace = Constants.XmlNamespace.MAML)] 34 | [XmlIgnore] 35 | public List Description { get; set; } = new List(); 36 | 37 | public bool Equals(DataType other) 38 | { 39 | if (other is null) 40 | return false; 41 | 42 | return ( 43 | string.Compare(Name, other.Name) == 0 && 44 | string.Compare(Uri, other.Uri) == 0 && 45 | Description.SequenceEqual(other.Description) 46 | ); 47 | } 48 | 49 | public override bool Equals(object other) 50 | { 51 | if (other is DataType dataType2) 52 | { 53 | return Equals(dataType2); 54 | } 55 | 56 | return false; 57 | } 58 | 59 | public override int GetHashCode() 60 | { 61 | // Valid? 62 | return Name.GetHashCode() ^ Uri.GetHashCode(); 63 | } 64 | 65 | public static bool operator == (DataType dataType1, DataType dataType2) 66 | { 67 | if (dataType1 is not null && dataType2 is not null) 68 | { 69 | return dataType1.Equals(dataType2); 70 | } 71 | 72 | return false; 73 | } 74 | 75 | public static bool operator != (DataType dataType1, DataType dataType2) 76 | { 77 | if (dataType1 is not null && dataType2 is not null) 78 | { 79 | return ! dataType1.Equals(dataType2); 80 | } 81 | 82 | return false; 83 | } 84 | 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /.ci/ci.yml: -------------------------------------------------------------------------------- 1 | name: Build-$(System.PullRequest.PullRequestNumber)-$(Date:yyyyMMdd)$(Rev:.rr) 2 | trigger: 3 | # Batch merge builds together while a merge build is running 4 | batch: true 5 | branches: 6 | include: 7 | - main 8 | - release/* 9 | - v1 10 | pr: 11 | branches: 12 | include: 13 | - main 14 | - release/* 15 | - v1 16 | 17 | resources: 18 | repositories: 19 | - repository: ComplianceRepo 20 | type: github 21 | endpoint: ComplianceGHRepo 22 | name: PowerShell/Compliance 23 | 24 | stages: 25 | - stage: Build 26 | displayName: Build PowerShell Package 27 | jobs: 28 | - job: BuildPkg 29 | displayName: Build Package 30 | pool: 31 | vmImage: windows-latest 32 | steps: 33 | - pwsh: | 34 | $(Build.SourcesDirectory)/build.ps1 -Clean 35 | displayName: Build 36 | 37 | - pwsh: | 38 | $files = (Get-ChildItem -Recurse '$(Build.SourcesDirectory)/out/Microsoft.PowerShell.PlatyPS' -File).FullName 39 | 40 | $files | ForEach-Object { 41 | Write-Host "##vso[artifact.upload containerfolder=Microsoft.PowerShell.PlatyPS;artifactname=Microsoft.PowerShell.PlatyPS]$_" 42 | } 43 | displayName: Publish build artifact 44 | timeoutInMinutes: 10 45 | 46 | - pwsh: | 47 | Register-PSRepository -Name local -SourceLocation '$(System.ArtifactsDirectory)' -Verbose -ErrorAction Ignore 48 | Publish-Module -Repository local -Path '$(Build.SourcesDirectory)/out/Microsoft.PowerShell.PlatyPS' -Verbose 49 | $nupkgFile = Get-ChildItem -Path '$(System.ArtifactsDirectory)/Microsoft.PowerShell.PlatyPS*.nupkg' 50 | 51 | Write-Verbose -Verbose "Uploading: $nupkgFile" 52 | Write-Host "##vso[artifact.upload containerfolder=nupkg;artifactname=nupkg]$nupkgFile" 53 | 54 | displayName: Create module .nupkg and upload 55 | 56 | - stage: Test 57 | displayName: Test Package 58 | jobs: 59 | - template: test.yml 60 | parameters: 61 | jobName: TestPkgWin 62 | displayName: PowerShell Core on Windows 63 | imageName: windows-2019 64 | 65 | - template: test.yml 66 | parameters: 67 | jobName: TestPkgWinPS 68 | displayName: Windows PowerShell on Windows 69 | imageName: windows-2019 70 | powershellExecutable: powershell 71 | 72 | - template: test.yml 73 | parameters: 74 | jobName: TestPkgUbuntuLatest 75 | displayName: PowerShell Core on Ubuntu Latest 76 | imageName: ubuntu-latest 77 | 78 | - template: test.yml 79 | parameters: 80 | jobName: TestPkgWinMacOS 81 | displayName: PowerShell Core on macOS 82 | imageName: macOS-latest 83 | 84 | - stage: compliance 85 | displayName: Compliance 86 | dependsOn: Build 87 | jobs: 88 | - job: Compliance_Job 89 | pool: 90 | vmImage: windows-latest 91 | steps: 92 | - checkout: self 93 | - checkout: ComplianceRepo 94 | - template: ci-compliance.yml@ComplianceRepo 95 | -------------------------------------------------------------------------------- /platyPS.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30717.126 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerShell.PlatyPS", "src\Microsoft.PowerShell.PlatyPS.csproj", "{E366DB7C-7A85-48CB-895F-390D50349923}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|x64 = Debug|x64 12 | Debug|x86 = Debug|x86 13 | Release|Any CPU = Release|Any CPU 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {E366DB7C-7A85-48CB-895F-390D50349923}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {E366DB7C-7A85-48CB-895F-390D50349923}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {E366DB7C-7A85-48CB-895F-390D50349923}.Debug|x64.ActiveCfg = Debug|Any CPU 21 | {E366DB7C-7A85-48CB-895F-390D50349923}.Debug|x64.Build.0 = Debug|Any CPU 22 | {E366DB7C-7A85-48CB-895F-390D50349923}.Debug|x86.ActiveCfg = Debug|Any CPU 23 | {E366DB7C-7A85-48CB-895F-390D50349923}.Debug|x86.Build.0 = Debug|Any CPU 24 | {E366DB7C-7A85-48CB-895F-390D50349923}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {E366DB7C-7A85-48CB-895F-390D50349923}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {E366DB7C-7A85-48CB-895F-390D50349923}.Release|x64.ActiveCfg = Release|Any CPU 27 | {E366DB7C-7A85-48CB-895F-390D50349923}.Release|x64.Build.0 = Release|Any CPU 28 | {E366DB7C-7A85-48CB-895F-390D50349923}.Release|x86.ActiveCfg = Release|Any CPU 29 | {E366DB7C-7A85-48CB-895F-390D50349923}.Release|x86.Build.0 = Release|Any CPU 30 | {22D8368F-AC97-4D1E-B310-101C29D82CEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {22D8368F-AC97-4D1E-B310-101C29D82CEE}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {22D8368F-AC97-4D1E-B310-101C29D82CEE}.Debug|x64.ActiveCfg = Debug|Any CPU 33 | {22D8368F-AC97-4D1E-B310-101C29D82CEE}.Debug|x64.Build.0 = Debug|Any CPU 34 | {22D8368F-AC97-4D1E-B310-101C29D82CEE}.Debug|x86.ActiveCfg = Debug|Any CPU 35 | {22D8368F-AC97-4D1E-B310-101C29D82CEE}.Debug|x86.Build.0 = Debug|Any CPU 36 | {22D8368F-AC97-4D1E-B310-101C29D82CEE}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {22D8368F-AC97-4D1E-B310-101C29D82CEE}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {22D8368F-AC97-4D1E-B310-101C29D82CEE}.Release|x64.ActiveCfg = Release|Any CPU 39 | {22D8368F-AC97-4D1E-B310-101C29D82CEE}.Release|x64.Build.0 = Release|Any CPU 40 | {22D8368F-AC97-4D1E-B310-101C29D82CEE}.Release|x86.ActiveCfg = Release|Any CPU 41 | {22D8368F-AC97-4D1E-B310-101C29D82CEE}.Release|x86.Build.0 = Release|Any CPU 42 | EndGlobalSection 43 | GlobalSection(SolutionProperties) = preSolution 44 | HideSolutionNode = FALSE 45 | EndGlobalSection 46 | GlobalSection(ExtensibilityGlobals) = postSolution 47 | SolutionGuid = {4FDA8CBD-1334-41C9-B63F-14BA7C7073AE} 48 | EndGlobalSection 49 | EndGlobal 50 | -------------------------------------------------------------------------------- /test/Pester/assets/Compare-CommandHelp.md: -------------------------------------------------------------------------------- 1 | --- 2 | document type: cmdlet 3 | title: Compare-CommandHelp 4 | Module Name: Microsoft.PowerShell.PlatyPS 5 | Locale: "en-US" 6 | PlatyPS schema version: 2024-05-01 7 | HelpUri: 8 | ms.date: 05/20/2024 9 | external help file: Microsoft.PowerShell.PlatyPS.dll-Help.xml 10 | --- 11 | 12 | # Compare-CommandHelp 13 | 14 | ## SYNOPSIS 15 | 16 | {{ Fill in the Synopsis }} 17 | 18 | ## SYNTAX 19 | 20 | ### __AllParameterSets 21 | 22 | ``` 23 | Compare-CommandHelp [-Reference] [-Difference] 24 | [-PropertyNamesToExclude ] [] 25 | ``` 26 | 27 | ## ALIASES 28 | 29 | This cmdlet has the following aliases, 30 | {{Insert list of aliases}} 31 | 32 | ## DESCRIPTION 33 | 34 | {{ Fill in the Description }} 35 | 36 | ## EXAMPLES 37 | 38 | ### My Example 39 | 40 | description 41 | 42 | ```powershell 43 | PS> write-output hi 44 | hi 45 | ``` 46 | 47 | description 2 48 | 49 | ```powershell 50 | PS> echo bye 51 | bye 52 | ``` 53 | 54 | description 3 55 | 56 | ## PARAMETERS 57 | 58 | ### -Difference 59 | 60 | ```yaml 61 | Type: CommandHelp 62 | DefaultValue: '' 63 | Globbing: false 64 | ParameterValue: [] 65 | Aliases: [] 66 | ParameterSets: 67 | - Name: (All) 68 | Position: 1 69 | IsRequired: true 70 | ValueFromPipeline: false 71 | ValueFromPipelineByPropertyName: true 72 | ValueFromRemainingArguments: false 73 | DontShow: false 74 | AcceptedValues: [] 75 | HelpMessage: '' 76 | ``` 77 | 78 | ### -PropertyNamesToExclude 79 | 80 | {{ Fill PropertyNamesToExclude Description }} 81 | 82 | ```yaml 83 | Type: String[] 84 | DefaultValue: '' 85 | Globbing: false 86 | ParameterValue: [] 87 | Aliases: [] 88 | ParameterSets: 89 | - Name: (All) 90 | Position: Named 91 | IsRequired: false 92 | ValueFromPipeline: false 93 | ValueFromPipelineByPropertyName: false 94 | ValueFromRemainingArguments: false 95 | DontShow: false 96 | AcceptedValues: [] 97 | HelpMessage: '' 98 | ``` 99 | 100 | ### -Reference 101 | 102 | {{ Fill Reference Description }} 103 | 104 | ```yaml 105 | Type: CommandHelp 106 | DefaultValue: '' 107 | Globbing: false 108 | ParameterValue: [] 109 | Aliases: [] 110 | ParameterSets: 111 | - Name: (All) 112 | Position: 0 113 | IsRequired: true 114 | ValueFromPipeline: false 115 | ValueFromPipelineByPropertyName: true 116 | ValueFromRemainingArguments: false 117 | DontShow: false 118 | AcceptedValues: [] 119 | HelpMessage: '' 120 | ``` 121 | 122 | ### CommonParameters 123 | 124 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, 125 | -InformationAction, -InformationVariable, -OutBuffer, -OutVariable, -PipelineVariable, 126 | -ProgressAction, -Verbose, -WarningAction, -WarningVariable. 127 | For more information, see 128 | [about_CommonParameters](https://go.microsoft.com/fwlink/?LinkID=113216). 129 | 130 | ## INPUTS 131 | 132 | ### System.Management.Automation.HiThere 133 | 134 | ## OUTPUTS 135 | 136 | ### System.Object 137 | 138 | ## NOTES 139 | 140 | Just a note 141 | 142 | ## RELATED LINKS 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /ThirdPartyNotices.txt: -------------------------------------------------------------------------------- 1 | Do Not Translate or Localize 2 | 3 | This file is based on or incorporates material from the projects listed below (Third Party IP). The original copyright notice and the license under which Microsoft received such Third Party IP, are set forth below. Such licenses and notices are provided for informational purposes only. Microsoft licenses the Third Party IP to you under the licensing terms for the Microsoft product. Microsoft reserves all other rights not expressly granted under this agreement, whether by implication, estoppel or otherwise. 4 | 5 | --------------------------------------------- 6 | File: YamlDotNet 7 | --------------------------------------------- 8 | 9 | https://github.com/aaubry/YamlDotNet 10 | 11 | Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Antoine Aubry and contributors 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy of 14 | this software and associated documentation files (the "Software"), to deal in 15 | the Software without restriction, including without limitation the rights to 16 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 17 | of the Software, and to permit persons to whom the Software is furnished to do 18 | so, subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be included in all 21 | copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | SOFTWARE. 30 | 31 | 32 | --------------------------------------------- 33 | File: libyaml 34 | --------------------------------------------- 35 | 36 | Copyright (c) 2006 Kirill Simonov 37 | 38 | Permission is hereby granted, free of charge, to any person obtaining a copy of 39 | this software and associated documentation files (the "Software"), to deal in 40 | the Software without restriction, including without limitation the rights to 41 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 42 | of the Software, and to permit persons to whom the Software is furnished to do 43 | so, subject to the following conditions: 44 | 45 | The above copyright notice and this permission notice shall be included in all 46 | copies or substantial portions of the Software. 47 | 48 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 49 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 50 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 51 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 52 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 53 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 54 | SOFTWARE. 55 | -------------------------------------------------------------------------------- /src/Diagnostics/DiagnosticMessage.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | 8 | namespace Microsoft.PowerShell.PlatyPS.Model 9 | { 10 | /// 11 | /// A diagnostic message. 12 | /// 13 | public class DiagnosticMessage 14 | { 15 | /// 16 | /// The message source. 17 | /// 18 | public DiagnosticMessageSource Source { get; set; } 19 | 20 | /// 21 | /// The diagnostic message. 22 | /// 23 | public List Message { get; set; } 24 | 25 | /// 26 | /// The diagnostic message level. 27 | /// 28 | public DiagnosticSeverity Severity { get; set; } 29 | 30 | /// 31 | /// The diagnostic message identifier; Something that identifies the the diagnostic message. 32 | /// This could be the name of a parameter, or a specific piece of syntax. 33 | /// This is useful for diagnostics that are common across multiple elements. 34 | /// 35 | public string Identifier { get; set; } 36 | 37 | /// 38 | /// The line number to associate with the message. 39 | /// 40 | public int Line { get; set; } 41 | 42 | /// 43 | /// Create a new diagnostic message. 44 | /// 45 | public DiagnosticMessage() 46 | { 47 | Identifier = string.Empty; 48 | Source = DiagnosticMessageSource.General; 49 | Identifier = string.Empty; 50 | Severity = DiagnosticSeverity.Information; 51 | Message = new List(); 52 | Line = -1; 53 | } 54 | 55 | /// 56 | /// Create a new diagnostic message. 57 | /// 58 | /// The Diagnostic message source. 59 | /// The diagnostic message. 60 | /// The diagnostic message level. 61 | /// The diagnostic message identifier. 62 | /// The line of source to associate with the diagnostic. 63 | public DiagnosticMessage(DiagnosticMessageSource source, string message, DiagnosticSeverity severity, string identifier, int line) 64 | { 65 | Source = source; 66 | Message = new List(); 67 | Message.Add(message); 68 | Severity = severity; 69 | Identifier = identifier; 70 | Line = line; 71 | } 72 | 73 | public override string ToString() 74 | { 75 | var message = string.IsNullOrEmpty(Identifier) ? Message[0] : Identifier; 76 | if (message.Length > 65) 77 | { 78 | message = message.Substring(0, 65) + "..."; 79 | } 80 | 81 | return string.Format("{0,-12} {1,-22} {2}", Severity.ToString(), Source.ToString(), message); 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Microsoft.PowerShell.PlatyPS 2 | 3 | **PlatyPS** is the tool that Microsoft uses to create the PowerShell content you get from `Get-Help` 4 | and build the content published as [PowerShell documentation][03] on Microsoft Learn. 5 | 6 | PowerShell help files are stored as [Microsoft Assistance Markup Language][05] (MAML), an XML 7 | format. **PlatyPS** simplifies the authoring process by allowing you to write the help files in 8 | Markdown, then convert to MAML. [Markdown][04] is widely used in the software industry, 9 | supported by many editors including **Visual Studio Code**, and easier to author. 10 | 11 | **Microsoft.PowerShell.PlatyPS** includes several improvements: 12 | 13 | - Re-write in C# leveraging [markdig][02] for parsing Markdown (the same library used by 14 | Microsoft Learn to render Markdown) 15 | - Provides a more accurate description of a PowerShell cmdlet and its parameters and includes 16 | information that was previously unavailable 17 | - Creates an object model of the help file that you can manipulate and supports chaining cmdlets for 18 | complex operations 19 | - Increased performance - processes 1000s of Markdown files in seconds 20 | 21 | ## Install Microsoft.PowerShell.PlatyPS 22 | 23 | PlatyPS runs on: 24 | 25 | - Windows PowerShell 5.1+ 26 | - PowerShell 7+ on Windows, Linux, and macOS 27 | 28 | Install the module from [PSGallery][06]. 29 | 30 | ```powershell 31 | Install-PSResource -Name Microsoft.PowerShell.PlatyPS 32 | ``` 33 | 34 | ## Layout of this repository 35 | 36 | ### Branches 37 | 38 | - `main`: Contains the source code for the new version of PlatyPS named 39 | **Microsoft.PowerShell.PlatyPS**. This is the current supported version of PlatyPS. You can find 40 | this version listed as [Microsoft.PowerShell.PlatyPS][06] in the PowerShell Gallery. 41 | - `v1`: Contains the source code for the legacy version of PlatyPS named **platyPS**. This version 42 | is no longer actively maintained, but it's available for reference and legacy support. The latest 43 | version of this code is 0.14.2. You can find this version listed as [platyPS][07] in the 44 | PowerShell Gallery. 45 | 46 | ### Folder structure 47 | 48 | - `docs` - contains design documentation for the PlatyPS project 49 | - `src` - contains the source code for the module 50 | - `test` - contains the tests files used to validate the code at build time 51 | - `tools` - contains tools used to build the module 52 | 53 | ## Contributing to the project 54 | 55 | If you are interested in contributing to the PlatyPS project, please see the 56 | [contribution guide][01]. 57 | 58 | ### Build the code 59 | 60 | Prerequisites for build: 61 | 62 | - PS7 63 | - .NET 8 SDK 64 | 65 | Prerequisites for test: 66 | 67 | - Pester 4.x (Pester 5.x is not supported) 68 | 69 | To build the source code, run the following command in the root of the repository: 70 | 71 | ```powershell 72 | .\build.ps1 -Clean 73 | ``` 74 | 75 | 76 | [01]: ./CONTRIBUTING.md 77 | [02]: https://github.com/xoofx/markdig 78 | [03]: https://learn.microsoft.com/powershell/scripting 79 | [04]: https://wikipedia.org/wiki/Markdown 80 | [05]: https://wikipedia.org/wiki/Microsoft_Assistance_Markup_Language 81 | [06]: https://www.powershellgallery.com/packages/Microsoft.PowerShell.PlatyPS 82 | [07]: https://www.powershellgallery.com/packages/platyPS 83 | -------------------------------------------------------------------------------- /test/Pester/GetMarkdownMetadata.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | $ErrorActionPreference = 'Stop' 5 | . $PSScriptRoot/CommonFunction.ps1 6 | 7 | Describe 'Get Metadata from CommandHelp' { 8 | Context 'Simple markdown file' { 9 | BeforeAll { 10 | Set-Content -Path "$TestDrive/foo.md" -Value @' 11 | --- 12 | external help file: Microsoft.PowerShell.Archive-help.xml 13 | keywords: powershell,cmdlet 14 | Locale: en-US 15 | Module Name: Microsoft.PowerShell.Archive 16 | ms.date: 02/20/2020 17 | online version: https://docs.microsoft.com/powershell/module/microsoft.powershell.archive/compress-archive?view=powershell-7&WT.mc_id=ps-gethelp 18 | schema: 2.0.0 19 | title: Compress-Archive 20 | --- 21 | 22 | # Get-Thing 23 | 24 | ## SYNOPSIS 25 | Gets the thing 26 | 27 | ## SYNTAX 28 | 29 | ### PSet1 (Default) 30 | 31 | ``` 32 | Get-Thing [] 33 | ``` 34 | 35 | ## DESCRIPTION 36 | 37 | The `Get-Thing` cmdlet get the thing. 38 | 39 | 40 | ## EXAMPLES 41 | 42 | ### Example 1: Get the thing 43 | 44 | In this example, `Get-Thing` gets the thing. 45 | 46 | ```powershell 47 | Get-Thing 48 | ``` 49 | 50 | ```Output 51 | thing 52 | ``` 53 | 54 | ## PARAMETERS 55 | 56 | ### CommonParameters 57 | 58 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, 59 | -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, 60 | -WarningAction, and -WarningVariable. For more information, see 61 | [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 62 | 63 | ## INPUTS 64 | 65 | ## OUTPUTS 66 | 67 | ## NOTES 68 | 69 | no notes 70 | 71 | ## RELATED LINKS 72 | 73 | '@ 74 | } 75 | 76 | It 'can read file with relative path' { 77 | try { 78 | Push-Location $TestDrive 79 | $ch = Import-MarkdownCommandHelp "./foo.md" 80 | $d = $ch.Metadata 81 | $d.Keys | Should -HaveCount 9 82 | } 83 | finally { 84 | Pop-Location 85 | } 86 | } 87 | 88 | It 'can parse out yaml snippet' { 89 | $ch = Import-MarkdownCommandHelp "$TestDrive/foo.md" 90 | $d = $ch.Metadata 91 | $expectedKeys = @("document type", "external help file", "keywords", "Locale", "Module Name", "ms.date", "HelpUri", "PlatyPS schema version", "title") 92 | $d.Keys | Should -HaveCount $expectedKeys.Count 93 | $d.Keys | Should -BeIn $expectedKeys 94 | $d["Locale"] | Should -Be 'en-US' 95 | } 96 | 97 | It 'will sort metadata keys when exporting' { 98 | $ch = Import-MarkdownCommandHelp -Path "${PSScriptRoot}/assets/get-date.md" 99 | $originalMetadataKeys = $ch.Metadata.Keys 100 | $sortedMetadataKeys = $originalMetadataKeys | Sort-Object 101 | $originalMetadataKeys | Should -Not -Be $sortedMetadataKeys 102 | $file = $ch | Export-MarkdownCommandHelp -OutputFolder ${TESTDRIVE} 103 | $ch2 = $file | Import-MarkdownCommandHelp 104 | $ch2.Metadata.Keys | Should -Be $sortedMetadataKeys 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/Microsoft.PowerShell.PlatyPS.psd1: -------------------------------------------------------------------------------- 1 | # 2 | # Module manifest for module 'platyPS' 3 | # 4 | # Generated by: PowerShell team 5 | # 6 | # Generated on: 2/4/2016 7 | # 8 | 9 | @{ 10 | 11 | RootModule = 'Microsoft.PowerShell.PlatyPS.psm1' 12 | # Version number of this module. 13 | # Do not edit the version. The version is updated by the build script. 14 | ModuleVersion = '1.0.1' 15 | 16 | # ID used to uniquely identify this module 17 | GUID = '0bdcabef-a4b7-4a6d-bf7e-d879817ebbff' 18 | 19 | # Author of this module 20 | Author = 'PowerShell team' 21 | 22 | # Company or vendor of this module 23 | CompanyName = 'Microsoft' 24 | 25 | # Copyright statement for this module 26 | Copyright = '(c) 2016 PowerShell team. All rights reserved.' 27 | 28 | # Description of the functionality provided by this module 29 | Description = 'Generate PowerShell External Help files from Markdown' 30 | 31 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess 32 | NestedModules = @('Dependencies/YamlDotNet.dll', 'Dependencies/Markdig.Signed.dll', 'Microsoft.PowerShell.PlatyPS.dll') 33 | 34 | FormatsToProcess = @('Microsoft.PowerShell.PlatyPS.Format.ps1xml') 35 | 36 | # Functions to export from this module 37 | FunctionsToExport = @( 38 | 'Show-HelpPreview', 39 | 'New-HelpCabinetFile' 40 | ) 41 | 42 | # Cmdlets to export from this module 43 | CmdletsToExport = @( 44 | 'New-MarkdownCommandHelp', 45 | 'Import-MamlHelp', 46 | 'Import-MarkdownCommandHelp', 47 | 'Import-MarkdownModuleFile', 48 | 'Import-YamlModuleFile', 49 | 'Import-YamlCommandHelp', 50 | 'Export-MamlCommandHelp', 51 | 'Export-MarkdownCommandHelp', 52 | 'Export-MarkdownModuleFile', 53 | 'Export-YamlCommandHelp', 54 | 'Export-YamlModuleFile', 55 | 'Export-MamlCommandHelp', 56 | 'Test-MarkdownCommandHelp', 57 | 'New-CommandHelp', 58 | 'New-MarkdownModuleFile', 59 | 'Compare-CommandHelp', 60 | 'Measure-PlatyPSMarkdown', 61 | 'Update-MarkdownCommandHelp', 62 | 'Update-MarkdownModuleFile', 63 | 'Update-CommandHelp' 64 | ) 65 | 66 | # Variables to export from this module 67 | VariablesToExport = @() 68 | 69 | # Aliases to export from this module 70 | AliasesToExport = @() 71 | 72 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 73 | PrivateData = @{ 74 | 75 | PSData = @{ 76 | 77 | ## Prerelease = '' 78 | 79 | # Tags applied to this module. These help with module discovery in online galleries. 80 | Tags = @('help', 'markdown', 'MAML', 'PSEdition_Core', 'PSEdition_Desktop') 81 | 82 | # A URL to the license for this module. 83 | LicenseUri = 'https://github.com/PowerShell/platyPS/blob/master/LICENSE' 84 | 85 | # A URL to the main website for this project. 86 | ProjectUri = 'https://github.com/PowerShell/platyPS' 87 | 88 | # A URL to an icon representing this module. 89 | # IconUri = '' 90 | 91 | # ReleaseNotes of this module 92 | # ReleaseNotes = '' 93 | 94 | } 95 | 96 | } 97 | 98 | # HelpInfo URI of this module 99 | HelpInfoURI = 'https://aka.ms/ps-modules-help' 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/Model/Example.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Text; 6 | 7 | namespace Microsoft.PowerShell.PlatyPS.Model 8 | { 9 | 10 | /// 11 | /// Class to represent the properties of an example in PowerShell help. 12 | /// 13 | public class Example : IEquatable 14 | { 15 | public string Title { get; set; } 16 | public string Remarks { get; set; } 17 | 18 | public Example(string title, string remarks) 19 | { 20 | Title = title; 21 | Remarks = remarks; 22 | } 23 | 24 | /// 25 | /// Create a new example from an existing one. 26 | /// 27 | /// The example to copy. 28 | public Example(Example example) 29 | { 30 | Title = example.Title; 31 | Remarks = example.Remarks; 32 | } 33 | 34 | internal string ToExampleItemString(string fmt, int serialNumber) 35 | { 36 | StringBuilder sb = Constants.StringBuilderPool.Get(); 37 | 38 | try 39 | { 40 | // sb.AppendFormat(Constants.ExampleItemHeaderTemplate, serialNumber, Title); 41 | sb.AppendFormat(fmt, serialNumber, Title); 42 | sb.AppendLine(); 43 | 44 | if (!string.IsNullOrEmpty(Remarks) && !string.Equals(Remarks, Environment.NewLine)) 45 | { 46 | sb.AppendLine(); 47 | sb.Append(Remarks); 48 | } 49 | 50 | return sb.ToString(); 51 | } 52 | finally 53 | { 54 | Constants.StringBuilderPool.Return(sb); 55 | } 56 | } 57 | 58 | public bool Equals(Example other) 59 | { 60 | if (other is null) 61 | { 62 | return false; 63 | } 64 | 65 | return (string.Compare(Title, other.Title, StringComparison.CurrentCulture) == 0 && string.Compare(Remarks, other.Remarks, StringComparison.CurrentCulture) == 0); 66 | } 67 | 68 | public override bool Equals(object other) 69 | { 70 | if (other is null) 71 | { 72 | return false; 73 | } 74 | 75 | if (other is Example example2) 76 | { 77 | return Equals(example2); 78 | } 79 | 80 | return false; 81 | } 82 | 83 | public override int GetHashCode() => (Title, Remarks).GetHashCode(); 84 | 85 | public static bool operator == (Example example1, Example example2) 86 | { 87 | if (example1 is not null && example2 is not null) 88 | { 89 | return example1.Equals(example2); 90 | } 91 | 92 | return false; 93 | } 94 | 95 | public static bool operator != (Example example1, Example example2) 96 | { 97 | if (example1 is not null && example2 is not null) 98 | { 99 | return ! example1.Equals(example2); 100 | } 101 | 102 | return false; 103 | } 104 | } 105 | } 106 | 107 | -------------------------------------------------------------------------------- /src/YamlWriter/CommandHelpYamlWriterHelper.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | using System.IO; 8 | using System.Text; 9 | using Microsoft.PowerShell.PlatyPS; 10 | using Microsoft.PowerShell.PlatyPS.Model; 11 | using Microsoft.PowerShell.PlatyPS.YamlWriter; 12 | 13 | namespace Microsoft.PowerShell.PlatyPS.Test 14 | { 15 | /// 16 | /// Write the CommandHelp object to a file in yaml format. 17 | /// This is used mostly for testing. 18 | /// 19 | public class CommandHelpYamlWriterHelper 20 | { 21 | /// 22 | /// Convert a markdown file to a yaml file. 23 | /// 24 | /// The path of the markdown file. 25 | /// The path of the output yaml file. 26 | public static void ConvertMarkdownToYaml(string markdownPath, string yamlPath) 27 | { 28 | var writerSettings = new WriterSettings(Encoding.UTF8, yamlPath); 29 | var yamlWriter = new CommandHelpYamlWriter(writerSettings); 30 | var commandHelp = (CommandHelp)MarkdownConverter.GetCommandHelpFromMarkdownFile(markdownPath); 31 | yamlWriter.Write(commandHelp, metadata: null); 32 | } 33 | 34 | /// 35 | /// Convert a CommandHelp object to a yaml file. 36 | /// 37 | /// 38 | /// 39 | public static void ConvertMarkdownToYaml(object help, string yamlPath) 40 | { 41 | if (help is CommandHelp commandHelp) 42 | { 43 | var writerSettings = new WriterSettings(Encoding.UTF8, yamlPath); 44 | var yamlWriter = new CommandHelpYamlWriter(writerSettings); 45 | yamlWriter.Write(commandHelp, metadata: null); 46 | } 47 | } 48 | 49 | /// 50 | /// Validate that the file is a valid yaml file. 51 | /// It does not validate that the file is a valid CommandHelp yaml file. 52 | /// 53 | /// The path to the yaml file. 54 | /// The resultant object, in the case of an exception it will return the exception. 55 | /// True if the file is valid yaml, false if not. 56 | public static bool TestYamlFile(string yamlPath, out object? result) 57 | { 58 | var yaml = File.ReadAllText(yamlPath); 59 | StringReader stringReader = new StringReader(yaml); 60 | var deserializer = new YamlDotNet.Serialization.DeserializerBuilder().Build(); 61 | 62 | result = null; 63 | bool isValid = false; 64 | 65 | try 66 | { 67 | var yamlObject = deserializer.Deserialize(stringReader); 68 | if (yamlObject is not null) 69 | { 70 | result = yamlObject; 71 | isValid = true; 72 | } 73 | } 74 | catch (Exception e) 75 | { 76 | result = e; 77 | } 78 | 79 | return isValid; 80 | } 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/MarkdownWriter/MarkdownConstants.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | namespace Microsoft.PowerShell.PlatyPS.Model 9 | { 10 | // MARKDOWN CONSTANTS 11 | internal static partial class Constants 12 | { 13 | internal const string MarkdownMetadataHeader = "---"; 14 | internal const string SchemaVersionMarkdown = "PlatyPS schema version: 2024-05-01"; 15 | internal const string mdSynopsisHeader = "## SYNOPSIS"; 16 | internal const string mdSyntaxHeader = "## SYNTAX"; 17 | internal const string mdDescriptionHeader = "## DESCRIPTION"; 18 | internal const string mdAliasHeader = "## ALIASES"; 19 | internal const string AliasMessage1 = "This cmdlet has the following aliases,"; 20 | internal const string AliasMessage2 = "{{Insert list of aliases}}"; 21 | internal const string AliasMessage = $"{AliasMessage1}\n {AliasMessage2}"; 22 | // internal const string AliasMessage = "This cmdlet has the following aliases,\n {{Insert list of aliases}}"; 23 | internal const string mdExamplesHeader = "## EXAMPLES"; 24 | internal const string mdParametersHeader = "## PARAMETERS"; 25 | internal const string mdCommonParametersHeader = "### CommonParameters"; 26 | internal const string mdWorkflowCommonParametersHeader = "### WorkflowCommonParameters"; 27 | internal const string mdInputsHeader = "## INPUTS"; 28 | internal const string mdOutputsHeader = "## OUTPUTS"; 29 | internal const string mdNotesHeader = "## NOTES"; 30 | internal const string mdRelatedLinksHeader = "## RELATED LINKS"; 31 | internal const string mdRelatedLinksFmt = "- [{0}]({1})"; 32 | 33 | internal const string mdExampleItemHeaderTemplate = "### Example {0}: {1}"; 34 | 35 | internal const string mdDefaultParameterSetHeaderTemplate = "### {0} (Default)"; 36 | internal const string mdParameterSetHeaderTemplate = "### {0}"; 37 | internal const string CodeBlock = "```"; 38 | 39 | internal const string GenericParameterBackTick = "`"; 40 | internal const string GenericParameterTypeNameStart = "`{0}["; 41 | internal const string GenericParameterTypeNameEnd = "]"; 42 | 43 | internal const string mdNotesItemHeaderTemplate = "### {0}"; 44 | 45 | internal const string mdModulePageModuleNameHeaderTemplate = "# {0} Module"; 46 | internal const string mdModulePageDescriptionHeader = "## Description"; 47 | internal const string mdModulePageCmdletHeaderTemplate = "## {0} Cmdlets"; 48 | internal const string mdModulePageCmdletLinkTemplate = "### [{0}]({1})"; 49 | 50 | // THIS IS MARKDOWN 51 | internal const string mdParameterYamlBlockWithAcceptedValues = @"### -{0} 52 | 53 | {1} 54 | 55 | ```yaml 56 | Type: {2} 57 | Parameter Sets: {3} 58 | Aliases: {4} 59 | Accepted values: {5} 60 | Required: {6} 61 | Position: {7} 62 | Default value: {8} 63 | Accept pipeline input: {9} 64 | Accept wildcard characters: {10} 65 | DontShow: {11} 66 | ```"; 67 | 68 | internal const string mdParameterYamlBlock = @"### -{0} 69 | 70 | {1} 71 | 72 | ```yaml 73 | Type: {2} 74 | Parameter Sets: {3} 75 | Aliases: {4} 76 | Required: {5} 77 | Position: {6} 78 | Default value: {7} 79 | Accept pipeline input: {8} 80 | Accept wildcard characters: {9} 81 | DontShow: {10} 82 | ```"; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Command/ImportYamlModuleFileCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Management.Automation; 8 | using Microsoft.PowerShell.PlatyPS.Model; 9 | 10 | namespace Microsoft.PowerShell.PlatyPS 11 | { 12 | /// 13 | /// Cmdlet to import a yaml file and convert it to a ModuleFile object. 14 | /// 15 | [Cmdlet(VerbsData.Import, "YamlModuleFile", DefaultParameterSetName = "Path", HelpUri = "")] 16 | [OutputType(typeof(ModuleFileInfo))] 17 | public sealed class ImportYamlModuleFileCommand : PSCmdlet 18 | { 19 | #region cmdlet parameters 20 | 21 | [Parameter(Mandatory=true, Position=0, ValueFromPipeline=true, ParameterSetName= "Path")] 22 | [ValidateNotNullOrEmpty] 23 | [SupportsWildcards] 24 | public string[] Path { get; set; } = Array.Empty(); 25 | 26 | [Parameter(Mandatory=true, ValueFromPipeline=true, ParameterSetName= "LiteralPath")] 27 | [Alias("PSPath", "LP")] 28 | [ValidateNotNullOrEmpty] 29 | public string[] LiteralPath { get; set; } = Array.Empty(); 30 | 31 | [Parameter] 32 | public SwitchParameter AsDictionary { get; set; } 33 | 34 | #endregion 35 | 36 | protected override void ProcessRecord() 37 | { 38 | List resolvedPaths; 39 | try 40 | { 41 | // This is a list because the resolution process can result in multiple paths (in the case of non-literal path). 42 | resolvedPaths = PathUtils.ResolvePath(this, ParameterSetName == "LiteralPath" ? LiteralPath : Path, ParameterSetName == "LiteralPath" ? true : false); 43 | } 44 | catch (Exception e) 45 | { 46 | WriteError(new ErrorRecord(e, "Could not resolve Path", ErrorCategory.InvalidOperation, ParameterSetName == "LiteralPath" ? LiteralPath : Path)); 47 | return; 48 | } 49 | 50 | // These should be resolved paths, whether -LiteralPath was used or not. 51 | foreach (string path in resolvedPaths) 52 | { 53 | if (AsDictionary) 54 | { 55 | if (YamlUtils.TryGetOrderedDictionaryFromText(File.ReadAllText(path), out var dictionaryResult)) 56 | { 57 | WriteObject(dictionaryResult); 58 | } 59 | else 60 | { 61 | WriteError(new ErrorRecord(new InvalidOperationException("DeserializationError"), "ImportYamlModuleFile,FailedToConvertYamlToDictionary", ErrorCategory.InvalidOperation, path)); 62 | } 63 | continue; 64 | } 65 | 66 | if (YamlUtils.TryReadModuleFile(path, out ModuleFileInfo? moduleFileInfo, out Exception? deserializationError)) 67 | { 68 | WriteObject(moduleFileInfo); 69 | } 70 | else 71 | { 72 | var wrappedException = new InvalidDataException($"Could not parse file as a module file. '{path}'", deserializationError); 73 | WriteError(new ErrorRecord(wrappedException, "ImportYamlModuleFile,FailedToConvertYamltoModuleFileInfo", ErrorCategory.InvalidOperation, path)); 74 | } 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /test/Pester/ParagraphFormatting.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | using namespace Microsoft.PowerShell.PlatyPS 5 | 6 | Describe "ParagraphFormatHelper" { 7 | It "Formats paragraph with default settings" { 8 | $settings = [ParagraphFormatSettings]::new() 9 | $line = "This is a test paragraph. This is a test paragraph. This is a test paragraph. This is a test paragraph. This is a test paragraph." 10 | $expected = "This is a test paragraph. This is a test paragraph. This is a test paragraph.", 11 | "This is a test paragraph. This is a test paragraph." 12 | 13 | $result = [ParagraphFormatHelper]::FormatParagraph($line, $settings) -split [Environment]::NewLine 14 | 15 | $result | Should -Be $expected 16 | } 17 | 18 | It "Formats paragraph with indentation" { 19 | $settings = [ParagraphFormatSettings]::new(80, 4, 0) 20 | $line = "This is a test paragraph. This is a test paragraph. This is a test paragraph. This is a test paragraph. This is a test paragraph." 21 | $expected = " This is a test paragraph. This is a test paragraph. This is a test", 22 | " paragraph. This is a test paragraph. This is a test paragraph." 23 | 24 | $result = [ParagraphFormatHelper]::FormatParagraph($line, $settings) -split [Environment]::NewLine 25 | 26 | $result | Should -Be $expected 27 | } 28 | 29 | It "Formats paragraph with first line indentation" { 30 | $settings = [ParagraphFormatSettings]::new(80, 0, 4) 31 | $line = "This is a test paragraph. This is a test paragraph. This is a test paragraph. This is a test paragraph. This is a test paragraph." 32 | $expected = " This is a test paragraph. This is a test paragraph. This is a test", 33 | "paragraph. This is a test paragraph. This is a test paragraph." 34 | $result = [ParagraphFormatHelper]::FormatParagraph($line, $settings) -split [System.Environment]::NewLine 35 | 36 | $result | Should -Be $expected 37 | } 38 | 39 | It "Formats paragraph with a very long word" { 40 | $settings = [ParagraphFormatSettings]::new(50, 4, 4) 41 | $line = "This is a test paragraph. This is a test paragraph. ThisIsAVeryLongWordWhichWillBeLongerThanTheAllowedLineLengthItShouldBeFormattedProperly. This is a test paragraph. This is a test paragraph. This is a test paragraph." 42 | $expected = " This is a test paragraph. This is a test", 43 | " paragraph.", 44 | " ThisIsAVeryLongWordWhichWillBeLongerThanTheAllowedLineLengthItShouldBeFormattedProperly.", 45 | " This is a test paragraph. This is a test", 46 | " paragraph. This is a test paragraph." 47 | $result = [ParagraphFormatHelper]::FormatParagraph($line, $settings) -split [System.Environment]::NewLine 48 | $result | Should -Be $expected 49 | } 50 | 51 | It "Formats an array of strings into a paragraph" { 52 | $settings = [ParagraphFormatSettings]::new(50, 4, 4) 53 | $lines = "This is a test paragraph.", "This is a test paragraph.", "This is a test paragraph.", "This is a test paragraph.", "This is a test paragraph." 54 | $expected = " This is a test paragraph. This is a test", 55 | " paragraph. This is a test paragraph. This is", 56 | " a test paragraph. This is a test paragraph." 57 | $result = [ParagraphFormatHelper]::FormatParagraph($lines, $settings) -split [System.Environment]::NewLine 58 | $result | Should -Be $expected 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Model/ParameterSet.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | 8 | namespace Microsoft.PowerShell.PlatyPS.Model 9 | { 10 | /// 11 | /// Class to represent the properties of a parameter in PowerShell help. 12 | /// 13 | public class ParameterSet : IEquatable 14 | { 15 | public string Name { get; set;} = string.Empty; 16 | public string Position { get; set; } = string.Empty; 17 | public bool IsRequired { get; set; } = false; 18 | public bool ValueFromPipeline { get; set; } = false; 19 | public bool ValueFromPipelineByPropertyName { get; set; } = false; 20 | public bool ValueFromRemainingArguments { get; set; } = false; 21 | 22 | public ParameterSet() 23 | { 24 | 25 | } 26 | 27 | public ParameterSet(string name) 28 | { 29 | Name = name; 30 | } 31 | 32 | /// 33 | /// Create a copy of a parameter set. 34 | /// 35 | /// The parameter set to copy. 36 | public ParameterSet(ParameterSet pSet) 37 | { 38 | Name = pSet.Name; 39 | Position = pSet.Position; 40 | IsRequired = pSet.IsRequired; 41 | ValueFromPipeline = pSet.ValueFromPipeline; 42 | ValueFromPipelineByPropertyName = pSet.ValueFromPipelineByPropertyName; 43 | ValueFromRemainingArguments = pSet.ValueFromRemainingArguments; 44 | } 45 | 46 | public bool Equals(ParameterSet other) 47 | { 48 | if (other is null) 49 | { 50 | return false; 51 | } 52 | 53 | return ( 54 | string.Compare(Name, other.Name, StringComparison.CurrentCulture) == 0 && 55 | string.Compare(Position, other.Position, StringComparison.CurrentCulture) == 0 && 56 | IsRequired == other.IsRequired && 57 | ValueFromPipeline == other.ValueFromPipeline && 58 | ValueFromPipelineByPropertyName == other.ValueFromPipelineByPropertyName && 59 | ValueFromRemainingArguments == other.ValueFromRemainingArguments); 60 | } 61 | 62 | public override bool Equals(object other) 63 | { 64 | if (other is null) 65 | { 66 | return false; 67 | } 68 | 69 | if (other is ParameterSet parameterSet2) 70 | { 71 | return Equals(parameterSet2); 72 | } 73 | 74 | return false; 75 | } 76 | 77 | public override int GetHashCode() 78 | { 79 | return (Name, Position, IsRequired, ValueFromPipeline, ValueFromPipelineByPropertyName, ValueFromRemainingArguments).GetHashCode(); 80 | } 81 | 82 | public static bool operator ==(ParameterSet parameterSet1, ParameterSet parameterSet2) 83 | { 84 | if (parameterSet1 is not null && parameterSet2 is not null) 85 | { 86 | return parameterSet1.Equals(parameterSet2); 87 | } 88 | 89 | return false; 90 | } 91 | 92 | public static bool operator !=(ParameterSet parameterSet1, ParameterSet parameterSet2) 93 | { 94 | if (parameterSet1 is not null && parameterSet2 is not null) 95 | { 96 | return ! parameterSet1.Equals(parameterSet2); 97 | } 98 | 99 | return false; 100 | } 101 | } 102 | } 103 | 104 | -------------------------------------------------------------------------------- /src/MamlWriter/Parameter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections.Generic; 5 | using System.Xml.Serialization; 6 | 7 | namespace Microsoft.PowerShell.PlatyPS.MAML 8 | { 9 | /// 10 | /// Represents a "parameter" element in a Powershell MAML help document. 11 | /// 12 | [XmlRoot("parameter", Namespace = Constants.XmlNamespace.Command)] 13 | public class Parameter 14 | { 15 | /// 16 | /// The parameter name ("maml:name"). 17 | /// 18 | [XmlElement("name", Namespace = Constants.XmlNamespace.MAML, Order = 0)] 19 | public string Name { get; set; } = string.Empty; 20 | 21 | /// 22 | /// The parameter's detailed description (one or more paragraphs; "maml:description/maml:para"). 23 | /// 24 | [XmlArray("description", Namespace = Constants.XmlNamespace.MAML, Order = 1)] 25 | [XmlArrayItem("para", Namespace = Constants.XmlNamespace.MAML)] 26 | public List Description { get; set; } = new List(); 27 | 28 | /// 29 | /// The parameter value information ("command:parameterValue"). 30 | /// 31 | [XmlElement("parameterValue", Namespace = Constants.XmlNamespace.Command, Order = 2)] 32 | public ParameterValue Value { get; set; } = new ParameterValue(); 33 | 34 | /// 35 | /// Is the parameter mandatory? 36 | /// 37 | [XmlAttribute("required")] 38 | public bool IsMandatory { get; set; } 39 | 40 | [XmlAttribute("variableLength")] 41 | public bool VariableLength { get; set; } 42 | 43 | /// 44 | /// Does the parameter support globbing (wildcards)? 45 | /// 46 | [XmlAttribute("globbing")] 47 | public bool SupportsGlobbing { get; set; } 48 | 49 | /// 50 | /// Can the parameter accept its value from the pipeline? 51 | /// 52 | /// 53 | /// Either "true (ByValue)", "true (ByPropertyName)", or "false". 54 | /// 55 | [XmlAttribute("pipelineInput")] 56 | public string PipelineInputString 57 | { 58 | set { throw new System.NotImplementedException(); } 59 | get 60 | { 61 | if (SupportsPipelineInput == PipelineInputType.ByValue && SupportsPipelineInput == PipelineInputType.ByPropertyName) 62 | { 63 | return "true (ByValue, ByPropertyName)"; 64 | } 65 | else if (SupportsPipelineInput == PipelineInputType.ByValue) 66 | { 67 | return "true (ByValue)"; 68 | } 69 | else if (SupportsPipelineInput == PipelineInputType.ByPropertyName) 70 | { 71 | return "true (ByPropertyName)"; 72 | } 73 | else 74 | { 75 | return "false"; 76 | } 77 | } 78 | } 79 | 80 | [XmlIgnore] 81 | public PipelineInputType SupportsPipelineInput { get; set; } 82 | 83 | /// 84 | /// The parameter's position. 85 | /// 86 | /// 87 | /// Either "named", or the 0-based position of the parameter. 88 | /// 89 | [XmlAttribute("position")] 90 | public string Position { get; set; } = "named"; 91 | 92 | /// 93 | /// The parameter's aliases. 94 | /// 95 | [XmlAttribute("aliases")] 96 | public string Aliases { get; set; } = "none"; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Common/TransformationAttribute.cs: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) Microsoft Corporation. 3 | // Licensed under the MIT License. 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Management.Automation; 9 | 10 | namespace Microsoft.PowerShell.PlatyPS 11 | { 12 | internal class CommandModuleTransformAttribute : ArgumentTransformationAttribute 13 | { 14 | public override object Transform(EngineIntrinsics engineIntrinsics, object inputData) 15 | { 16 | throw new NotImplementedException(); 17 | } 18 | 19 | internal object AttemptParameterConvert(EngineIntrinsics engineIntrinsics, string name, Type type) 20 | { 21 | using (var powershell = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace)) 22 | { 23 | if (type == typeof(PSModuleInfo)) 24 | { 25 | var results = powershell.AddCommand("Get-Module").AddParameter("Name", name).Invoke(); 26 | powershell.Commands.Clear(); 27 | if (results.Count == 0) 28 | { 29 | results = powershell.AddCommand("Import-Module").AddParameter("Name", name).AddParameter("PassThru").Invoke(); 30 | powershell.Commands.Clear(); 31 | } 32 | 33 | if (results.Count == 1 && results[0] is PSModuleInfo module) 34 | { 35 | return module; 36 | } 37 | } 38 | 39 | if (type == typeof(CommandInfo)) 40 | { 41 | var cmd = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace).AddCommand("Get-Command").AddParameter("Name", name).Invoke().First(); 42 | if (cmd is not null) 43 | { 44 | return cmd; 45 | } 46 | } 47 | } 48 | 49 | // We could not convert it, just return it for parameter binding. 50 | return name; 51 | } 52 | 53 | } 54 | 55 | internal class StringToCommandInfoTransformationAttribute : CommandModuleTransformAttribute 56 | { 57 | public override object Transform(EngineIntrinsics engineIntrinsics, object inputData) 58 | { 59 | if (inputData is string cmdName) 60 | { 61 | return AttemptParameterConvert(engineIntrinsics, cmdName, typeof(CommandInfo)); 62 | } 63 | if (inputData is Array cmdArray) 64 | { 65 | List cmdList = new(); 66 | foreach (var cmd in cmdArray) 67 | { 68 | cmdList.Add(AttemptParameterConvert(engineIntrinsics, cmd.ToString(), typeof(CommandInfo))); 69 | } 70 | return cmdList.ToArray(); 71 | } 72 | return inputData; 73 | } 74 | } 75 | 76 | internal sealed class StringToPsModuleInfoTransformationAttribute : CommandModuleTransformAttribute 77 | { 78 | public override object Transform(EngineIntrinsics engineIntrinsics, object inputData) 79 | { 80 | if (inputData is string moduleName) 81 | { 82 | return AttemptParameterConvert(engineIntrinsics, moduleName, typeof(PSModuleInfo)); 83 | } 84 | 85 | if (inputData is Array moduleArray) 86 | { 87 | List moduleList = new(); 88 | foreach (var mod in moduleArray) 89 | { 90 | moduleList.Add(AttemptParameterConvert(engineIntrinsics, mod.ToString(), typeof(PSModuleInfo))); 91 | } 92 | return moduleList.ToArray(); 93 | } 94 | 95 | return inputData; 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /test/Pester/ExportMarkdownModuleFile.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | Describe "Export-MarkdownModuleFile" { 5 | BeforeAll { 6 | $modFiles = Get-ChildItem -File "${PSScriptRoot}/assets" | Where-Object { $_.extension -eq ".md" -and $_.name -notmatch "-" -and $_.Name -notmatch "Bad.Metadata.Order" } 7 | $modFileNames = $modFiles.Foreach({$_.Name}) 8 | $modObjects = $modFiles.FullName | Import-MarkdownModuleFile 9 | $testModuleFile = $modObjects | Where-Object { $_.title -match 'Microsoft.PowerShell.Archive' } 10 | } 11 | 12 | Context "Basic Operation" { 13 | It "Should be able to export a module file" { 14 | $null = $modObjects[0] | Export-MarkdownModuleFile -outputFolder ${TestDrive} 15 | $moduleName = $modObjects[0].Module 16 | $expectedFile = "${TestDrive}/${moduleName}/${moduleName}.md" 17 | $expectedFile | Should -Exist 18 | } 19 | 20 | It "Should produce a warning if the file already exists" { 21 | $testModuleFile | Export-MarkdownModuleFile -outputFolder ${TestDrive} 22 | $result = $testModuleFile | Export-MarkdownModuleFile -outputFolder ${TestDrive} 3>&1 23 | # this is a warning object, make it a string 24 | $result | Should -BeOfType System.Management.Automation.WarningRecord 25 | $result.Message | Should -Be "'Microsoft.PowerShell.Archive' exists, skipping. Use -Force to overwrite." 26 | } 27 | 28 | It "Should be able to export a number of module files" { 29 | $outputFiles = $modObjects | Export-MarkdownModuleFile -outputFolder "${TestDrive}/group" 30 | $outputFiles.Count | Should -Be $modObjects.Count 31 | } 32 | } 33 | 34 | Context "File Contents" { 35 | BeforeAll { 36 | $outputFile = $modObjects[3] | Export-MarkdownModuleFile -outputFolder ${TestDrive} 37 | $testMF = Import-MarkdownModuleFile $outputFile.FullName 38 | } 39 | 40 | # This relies on implementation of IEquatable 41 | It "An exported module file should be equivalent to its source using IEquatable" { 42 | $modObjects[3] -eq $testMF | Should -Be $true 43 | } 44 | 45 | # Now check the hard way 46 | It "Should have the same metadata value for key ''" -TestCases $( 47 | @{ Name = "HelpInfoUri" } 48 | @{ Name = "Help Version" } 49 | @{ Name = "Locale" } 50 | @{ Name = "Module Guid" } 51 | @{ Name = "Module Name" } 52 | @{ Name = "ms.date" } 53 | @{ Name = "PlatyPS schema version" } 54 | @{ Name = "title" } 55 | @{ Name = "document type" } 56 | ) { 57 | param ($name) 58 | $testMF.Metadata[$name] | Should -Be $modObjects[3].Metadata[$name] 59 | } 60 | 61 | It "Should have the same value for the property ''" -TestCases $( 62 | @{ Name = "Description" } 63 | @{ Name = "Locale" } 64 | @{ Name = "Module" } 65 | @{ Name = "ModuleGuid" } 66 | @{ Name = "Title" } 67 | ) { 68 | param ($name) 69 | $testMF.$name | Should -Be $modObjects[3].$name 70 | } 71 | 72 | It "Should have the same values for the command ''" -TestCases $( 73 | $offset = 0 74 | $modObjects[3].CommandGroups[0].Commands | foreach-object { 75 | @{ Offset = $offset; Name = $_.Name; Link = $_.Link; Description = $_.Description } 76 | $offset++ 77 | } 78 | ) { 79 | param ($offset, $name, $link, $description) 80 | $testMF.CommandGroups[0].Commands[$offset].Name | Should -Be $name 81 | $testMF.CommandGroups[0].Commands[$offset].Link | Should -Be $link 82 | $testMF.CommandGroups[0].Commands[$offset].Description | Should -Be $description 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | .vs/ 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.sln.docstates 10 | 11 | # Build results 12 | [Dd]ebug/ 13 | [Dd]ebugPublic/ 14 | [Rr]elease/ 15 | x64/ 16 | build/ 17 | bld/ 18 | [Bb]in/ 19 | [Oo]bj/ 20 | 21 | # Roslyn cache directories 22 | *.ide/ 23 | 24 | # MSTest test Results 25 | [Tt]est[Rr]esult*/ 26 | [Bb]uild[Ll]og.* 27 | 28 | #NUNIT 29 | *.VisualState.xml 30 | TestResult.xml 31 | xunit.xml 32 | 33 | #Pester 34 | Pester.Tests.xml 35 | pester.tests.xml 36 | 37 | # Build Results of an ATL Project 38 | [Dd]ebugPS/ 39 | [Rr]eleasePS/ 40 | dlldata.c 41 | 42 | *_i.c 43 | *_p.c 44 | *_i.h 45 | *.ilk 46 | *.meta 47 | *.obj 48 | *.pch 49 | *.pdb 50 | *.pgc 51 | *.pgd 52 | *.rsp 53 | *.sbr 54 | *.tlb 55 | *.tli 56 | *.tlh 57 | *.tmp 58 | *.tmp_proj 59 | *.log 60 | *.vspscc 61 | *.vssscc 62 | .builds 63 | *.pidb 64 | *.svclog 65 | *.scc 66 | 67 | # Chutzpah Test files 68 | _Chutzpah* 69 | 70 | # Visual C++ cache files 71 | ipch/ 72 | *.aps 73 | *.ncb 74 | *.opensdf 75 | *.sdf 76 | *.cachefile 77 | 78 | # Visual Studio profiler 79 | *.psess 80 | *.vsp 81 | *.vspx 82 | 83 | # TFS 2012 Local Workspace 84 | $tf/ 85 | 86 | # Guidance Automation Toolkit 87 | *.gpState 88 | 89 | # ReSharper is a .NET coding add-in 90 | _ReSharper*/ 91 | *.[Rr]e[Ss]harper 92 | *.DotSettings.user 93 | 94 | # JustCode is a .NET coding addin-in 95 | .JustCode 96 | 97 | # TeamCity is a build add-in 98 | _TeamCity* 99 | 100 | # DotCover is a Code Coverage Tool 101 | *.dotCover 102 | 103 | # NCrunch 104 | _NCrunch_* 105 | .*crunch*.local.xml 106 | 107 | # MightyMoose 108 | *.mm.* 109 | AutoTest.Net/ 110 | 111 | # Web workbench (sass) 112 | .sass-cache/ 113 | 114 | # Installshield output folder 115 | [Ee]xpress/ 116 | 117 | # DocProject is a documentation generator add-in 118 | DocProject/buildhelp/ 119 | DocProject/Help/*.HxT 120 | DocProject/Help/*.HxC 121 | DocProject/Help/*.hhc 122 | DocProject/Help/*.hhk 123 | DocProject/Help/*.hhp 124 | DocProject/Help/Html2 125 | DocProject/Help/html 126 | 127 | # Click-Once directory 128 | publish/ 129 | 130 | # Publish Web Output 131 | *.[Pp]ublish.xml 132 | *.azurePubxml 133 | ## TODO: Comment the next line if you want to checkin your 134 | ## web deploy settings but do note that will include unencrypted 135 | ## passwords 136 | #*.pubxml 137 | 138 | # NuGet Packages Directory 139 | packages/* 140 | ## TODO: If the tool you use requires repositories.config 141 | ## uncomment the next line 142 | #!packages/repositories.config 143 | 144 | # Enable "build/" folder in the NuGet Packages folder since 145 | # NuGet packages use it for MSBuild targets. 146 | # This line needs to be after the ignore of the build folder 147 | # (and the packages folder if the line above has been uncommented) 148 | !packages/build/ 149 | 150 | # Windows Azure Build Output 151 | csx/ 152 | *.build.csdef 153 | 154 | # Windows Store app package directory 155 | AppPackages/ 156 | 157 | # Others 158 | sql/ 159 | *.Cache 160 | ClientBin/ 161 | [Ss]tyle[Cc]op.* 162 | ~$* 163 | *~ 164 | *.dbmdl 165 | *.dbproj.schemaview 166 | *.pfx 167 | *.publishsettings 168 | node_modules/ 169 | 170 | # RIA/Silverlight projects 171 | Generated_Code/ 172 | 173 | # Backup & report files from converting an old project file 174 | # to a newer Visual Studio version. Backup files are not needed, 175 | # because we have git ;-) 176 | _UpgradeReport_Files/ 177 | Backup*/ 178 | UpgradeLog*.XML 179 | UpgradeLog*.htm 180 | 181 | # SQL Server files 182 | *.mdf 183 | *.ldf 184 | 185 | # Business Intelligence projects 186 | *.rdl.data 187 | *.bim.layout 188 | *.bim_*.settings 189 | 190 | # Microsoft Fakes 191 | FakesAssemblies/ 192 | 193 | # LightSwitch generated files 194 | GeneratedArtifacts/ 195 | _Pvt_Extensions/ 196 | ModelManifest.xml 197 | 198 | # Out project specific stuff 199 | generatedMarkdown.txt 200 | out/ 201 | publish/ 202 | 203 | # For developing in VSCode 204 | .vscode/launch.json 205 | 206 | # exclude the module nupkg 207 | Microsoft.PowerShell.PlatyPS.*.nupkg -------------------------------------------------------------------------------- /src/Common/ParagraphFormatHelper.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Text; 6 | using Microsoft.PowerShell.PlatyPS; 7 | using Microsoft.PowerShell.PlatyPS.Model; 8 | 9 | namespace Microsoft.PowerShell.PlatyPS 10 | { 11 | public class ParagraphFormatSettings 12 | { 13 | /// 14 | /// The maximum length of a line in the paragraph. 15 | /// This includes indentation. 16 | /// 17 | public ushort LineLength { get; set; } 18 | 19 | /// 20 | /// How much indentation to add to each line. 21 | /// 22 | public byte Indentation { get; set; } 23 | 24 | /// 25 | /// how much indentation to add to the first line. 26 | /// 27 | public byte FirstLineIndentation { get; set; } 28 | 29 | /// 30 | /// The default settings for a paragraph arg: 31 | /// LineLength = 80 32 | /// No indentation 33 | /// No first line indentation 34 | /// 35 | public ParagraphFormatSettings() 36 | { 37 | LineLength = 80; 38 | Indentation = 0; 39 | FirstLineIndentation = 0; 40 | } 41 | 42 | public ParagraphFormatSettings(ushort lineLength, byte indentation, byte firstLineIndentation) 43 | { 44 | LineLength = lineLength; 45 | Indentation = indentation; 46 | FirstLineIndentation = firstLineIndentation; 47 | } 48 | } 49 | 50 | /// 51 | /// Helper class to format a string into a paragraph. 52 | /// It can also indent the paragraph, and add extra indentation to the first line. 53 | /// 54 | public class ParagraphFormatHelper 55 | { 56 | public static string FormatParagraph(string input, ParagraphFormatSettings settings) 57 | { 58 | var outputLine = Constants.StringBuilderPool.Get(); 59 | var currentLine = Constants.StringBuilderPool.Get(); 60 | 61 | if (settings.Indentation > 0) 62 | { 63 | currentLine.Append(new string(' ', settings.Indentation)); 64 | } 65 | 66 | if (settings.FirstLineIndentation > 0) 67 | { 68 | currentLine.Append(new string(' ', settings.FirstLineIndentation)); 69 | } 70 | 71 | string[] words = input.Split(new char[]{' '}, StringSplitOptions.RemoveEmptyEntries); 72 | foreach(string word in words) 73 | { 74 | if (currentLine.Length + word.Length + 1 > settings.LineLength) 75 | { 76 | outputLine.AppendLine(currentLine.ToString().TrimEnd()); 77 | currentLine.Clear(); 78 | currentLine.Append(new string(' ', settings.Indentation)); 79 | } 80 | 81 | currentLine.Append(word); 82 | currentLine.Append(' '); 83 | } 84 | 85 | // Only add the last line if it's not empty 86 | if (currentLine.Length > 0) 87 | { 88 | outputLine.Append(currentLine.ToString().TrimEnd()); 89 | } 90 | 91 | var outputLineString = outputLine.ToString(); 92 | Constants.StringBuilderPool.Return(currentLine); 93 | Constants.StringBuilderPool.Return(outputLine); 94 | return outputLineString; 95 | } 96 | 97 | /// 98 | /// Take a string array and join it into a single string, then format it into a paragraph. 99 | /// 100 | /// 101 | /// 102 | /// 103 | public static string FormatParagraph(string[] strings, ParagraphFormatSettings settings) 104 | { 105 | return FormatParagraph(string.Join(" ", strings), settings); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/Model/InputOutput.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | using System.Linq; 8 | using System.Management.Automation.Language; 9 | 10 | namespace Microsoft.PowerShell.PlatyPS.Model 11 | { 12 | /// 13 | /// Class to represent the properties of a input or and output type in PowerShell help. 14 | /// 15 | public class InputOutput : IEquatable 16 | { 17 | public string Typename { get; set; } = string.Empty; 18 | public string Description { get; set; } = string.Empty; 19 | 20 | public InputOutput(string typename, string description) 21 | { 22 | Typename = typename; 23 | Description = description; 24 | } 25 | 26 | private bool EqualityIncludeDescription = false; 27 | 28 | public void IncludeDescriptionForEqualityCheck() 29 | { 30 | EqualityIncludeDescription = true; 31 | } 32 | 33 | public void ExcludeDescriptionForEqualityCheck() 34 | { 35 | EqualityIncludeDescription = false; 36 | } 37 | 38 | public bool GetDescriptionCheckState() 39 | { 40 | return EqualityIncludeDescription; 41 | } 42 | 43 | /// 44 | /// Create a new inputoutput object from an existing one. 45 | /// 46 | /// the inputoutput to copy. 47 | public InputOutput(InputOutput inputOutput) 48 | { 49 | Typename = inputOutput.Typename; 50 | Description = inputOutput.Description; 51 | } 52 | 53 | internal string ToInputOutputString(string fmt) 54 | { 55 | StringBuilder sb = Constants.StringBuilderPool.Get(); 56 | 57 | try 58 | { 59 | sb.AppendLine(string.Format(fmt, Typename)); 60 | sb.AppendLine(); 61 | if (!string.IsNullOrEmpty(Description)) 62 | { 63 | sb.AppendLine(Description); 64 | sb.AppendLine(); 65 | } 66 | 67 | return sb.ToString().Trim(); 68 | } 69 | finally 70 | { 71 | Constants.StringBuilderPool.Return(sb); 72 | } 73 | } 74 | 75 | public bool Equals(InputOutput other) 76 | { 77 | if (other is null) 78 | { 79 | return false; 80 | } 81 | 82 | var typenameIsSame = other.Typename == Typename; 83 | var areEqual = EqualityIncludeDescription ? other.Description == Description && typenameIsSame : typenameIsSame; 84 | return areEqual; 85 | } 86 | 87 | public override bool Equals(object other) 88 | { 89 | if (other is null) 90 | { 91 | return false; 92 | } 93 | 94 | if (other is InputOutput inputOutput2) 95 | { 96 | return Equals(inputOutput2); 97 | } 98 | 99 | return false; 100 | } 101 | 102 | public override int GetHashCode() 103 | { 104 | return (Typename, Description).GetHashCode(); 105 | } 106 | 107 | public static bool operator ==(InputOutput inputOutput1, InputOutput inputOutput2) 108 | { 109 | if (inputOutput1 is not null && inputOutput2 is not null) 110 | { 111 | return inputOutput1.Equals(inputOutput2); 112 | } 113 | 114 | return false; 115 | } 116 | 117 | public static bool operator !=(InputOutput inputOutput1, InputOutput inputOutput2) 118 | { 119 | if (inputOutput1 is not null && inputOutput2 is not null) 120 | { 121 | return ! inputOutput1.Equals(inputOutput2); 122 | } 123 | 124 | return false; 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /test/Pester/ExportYamlModuleFile.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | Describe "Export-YamlModuleFile tests" { 5 | BeforeAll { 6 | $moduleFileNames = "Microsoft.PowerShell.Archive.md", "Microsoft.PowerShell.Core.md", 7 | "Microsoft.PowerShell.Diagnostics.md", "Microsoft.PowerShell.Host.md", "Microsoft.PowerShell.Management.md", "Microsoft.PowerShell.Security.md", 8 | "Microsoft.PowerShell.Utility.md", "Microsoft.WSMan.Management.md", "PSDiagnostics.md", "PSReadLine.md", "ThreadJob.md" 9 | $moduleFileList = $moduleFileNames.ForEach({"${PSScriptRoot}/assets/$_"}) 10 | $moduleFileInfos = Import-MarkdownModuleFile -Path $moduleFileList 11 | } 12 | 13 | Context "Basic tests" { 14 | It "Can export yaml module file '' in the proper location" -TestCases @( 15 | $moduleFileList.ForEach({@{ name = ([io.path]::GetFileName($_)); path = "$_" }}) 16 | ) { 17 | param ($path, $name) 18 | $outputFile = Import-MarkdownModuleFile -Path $path | Export-YamlModuleFile -OutputFolder ${TestDrive} 19 | $outputFile | Should -Exist 20 | $moduleName = [io.path]::GetFileNameWithoutExtension($outputFile.FullName) 21 | $moduleFile = [io.path]::GetFileName($outputFile.FullName) 22 | $expectedLocation = [io.Path]::Combine($TestDrive, $moduleName, $moduleFile) 23 | $outputFile.FullName | Should -Be $expectedLocation 24 | } 25 | 26 | It "Will add additional metadata if supplied" { 27 | $outputFolder = "${TestDrive}/new0" 28 | $newMetadata = @{ newkey1 = "new metadata 1"; "new key 2" = "new metadata[withspecialchar]"} 29 | $outFile = $moduleFileInfos[0] | Export-YamlModuleFile -OutputFolder ${outputFolder} -Metadata $newMetadata 30 | $readModuleFile = Import-YamlModuleFile $outFile 31 | $readModuleFile.Metadata['newkey1'] | Should -BeExactly $newMetadata['newkey1'] 32 | $readModuleFile.Metadata['new key 2'] | Should -BeExactly $newMetadata['new key 2'] 33 | } 34 | 35 | It "Will not create a new file if one already exists" { 36 | $outputFolder = "${TestDrive}/new1" 37 | $outFile = $moduleFileInfos | Export-YamlModuleFile -OutputFolder ${outputFolder} 38 | $outFile | Should -Exist 39 | Set-Content $outFile[0].FullName -Value $null 40 | $result = $moduleFileInfos[0] | Export-YamlModuleFile -OutputFolder ${outputFolder} 3>&1 41 | $result | Should -BeOfType [System.Management.Automation.WarningRecord] 42 | $result.Message | Should -Be "'Microsoft.PowerShell.Archive' exists, skipping. Use -Force to overwrite." 43 | (Get-ChildItem $outFile[0]).Length | Should -Be 0 44 | } 45 | 46 | It 'Can export a group of module files when piped' { 47 | $outputFolder = "${TestDrive}/new2" 48 | $moduleFileInfos | Export-YamlModuleFile -OutputFolder ${outputFolder} 49 | (Get-ChildItem $outputFolder).Count | Should -Be $moduleFileInfos.Count 50 | } 51 | 52 | It 'Can export a group of module files when provided as argument' { 53 | $outputFolder = "${TestDrive}/new3" 54 | Export-YamlModuleFile $moduleFileInfos -OutputFolder ${outputFolder} 55 | (Get-ChildItem $outputFolder).Count | Should -Be $moduleFileInfos.Count 56 | } 57 | } 58 | 59 | Context "Round trip tests (export and then import)" { 60 | BeforeAll { 61 | $yamlFiles = $moduleFileInfos | Export-YamlModuleFile -output "${TestDrive}/new4" 62 | } 63 | 64 | It "Should be able to round trip module info file ''" -TestCases $( 65 | for($i = 0; $i -lt $moduleFileInfos.Count; $i++) { 66 | @{ Source = $moduleFileInfos[$i]; Target = $yamlFiles[$i].FullName; Name = $yamlFiles[$i].Name} 67 | } 68 | ) { 69 | param ( $source, $target, $name ) 70 | $expected = $source 71 | $observed = Import-YamlModuleFile -Path $target 72 | $observed | Should -Be $expected 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /docs/archive/Schema-Notes.md: -------------------------------------------------------------------------------- 1 | This the current layout of the markdown for a Cmdlet with notes: 2 | 3 | YAML Frontmatter 4 | 5 | - Need to not error on PlatyPS required keys (external help file, Locale, Module Name, ms.date, 6 | online version, schema, title) 7 | - What should we do about description? - currently copied by OPS from Description below 8 | - Mapping to existing JSON schema: properties/metadata 9 | 10 | Title 11 | 12 | - Type: H1 13 | - Required: Y 14 | - Count: 1 15 | - Mapping to existing JSON schema: properties/name 16 | 17 | Synopsis 18 | 19 | - Type: H2 20 | - Required: Y 21 | - Count: 1 22 | - Mapping to existing JSON schema: properties/summary 23 | 24 | Syntax 25 | 26 | - Type: H2 27 | - Required: Y 28 | - Count: 1 29 | - Required: No changes 30 | - Mapping to existing JSON schema: properties/syntaxes 31 | - Notes: Contains 1 or more of the following: 32 | - Parameter Set name 33 | - Type: H3 34 | - Required: Y 35 | - Count: 1-N 36 | - Parameter Sets - should be sorted starting with Default, then by Alpha 37 | - Parameters (in syntax block) 38 | - Syntax block 39 | - What format do you want? - Currently MAML2Yaml is using the strings from the code block instead 40 | of a Yaml data model. Prefer the string model. 41 | 42 | Description 43 | 44 | - Type: H2 45 | - Required: Y 46 | - Count: 1 47 | - Mapping to existing JSON schema: properties/description 48 | - Allow any markdown content type except Headers 49 | 50 | Examples 51 | 52 | - Type: H2 53 | - Required: Y 54 | - Count: 1 55 | - Mapping to existing JSON schema: properties/examples 56 | - Notes: Contains 1 or more of the following 57 | - Example 58 | - Type: H3 59 | - Required: Y 60 | - Count: 1-N 61 | - Should require one code block at minimum per example 62 | - Should allow a mix of markdown code blocks and paragraphs in any order 63 | 64 | Parameters 65 | 66 | - Type: H2 67 | - Required: Y 68 | - Count: 1 69 | - Mapping to existing JSON schema: properties/parameters 70 | - Parameters should be sorted Alphabetically followed by `-Confirm` and `-WhatIf` (when included). 71 | PlatyPS has the `-AlphabeticParamsOrder` parameter that produces this order. 72 | - Notes: Contains 1 or more of the following 73 | - Parameter 74 | - Type: H3 75 | - Required: Y 76 | - Count: 1-N 77 | - Required: Yaml block should include: 78 | - Parameter Set Name 79 | - Position can be different per parameter set 80 | - Add **AcceptedValues** - Should display enumerated values of parameter (like ValidateSet) 81 | - maps to properties/parameters/parameterValueGroup? 82 | - Add **ConfirmImpact** - Impact severity should be reflected and displayed to inform defaults 83 | for `-Confirm` 84 | - what is **valueFrom** in JSON schema? 85 | - **pipelineInput** is currently a boolean. The value can be different per parameter set to 86 | the type needs to be a string 87 | 88 | Common Parameters 89 | 90 | - Type: H3 91 | - Required: Y 92 | - Count: 1 93 | - Notes: 94 | - this is missing from the schema - need to define it 95 | - The content should be the markdown description provided in the file 96 | 97 | Inputs 98 | 99 | - Type: H2 100 | - Required: Y 101 | - Count: 1 102 | - Mapping to existing JSON schema: properties/inputs 103 | - Notes: Contains 0 or more of the following: 104 | - Input type 105 | - Type: H3 106 | - Required: N 107 | - Count: 0-N 108 | 109 | Outputs 110 | 111 | - Type: H2 112 | - Required: Y 113 | - Count: 1 114 | - Mapping to existing JSON schema: properties/outputs 115 | - Notes: Contains 0 or more of the following: 116 | - Output type 117 | - Type: H3 118 | - Required: N 119 | - Count: 0-N 120 | 121 | Notes 122 | 123 | - Type: H2 124 | - Required: N 125 | - Mapping to existing JSON schema: properties/notes 126 | - Count: 1 header with 0 or more markdown elements (excluding header types) 127 | 128 | Related links 129 | 130 | - Type: H3 131 | - Required: Y 132 | - Count: 1 133 | - Mapping to existing JSON schema: properties/links 134 | - Link list should use a bullet list 135 | - Should support markdown paragraphs not just links 136 | -------------------------------------------------------------------------------- /test/Pester/assets/Microsoft.SystemCenter.ServiceManagementAutomation.md: -------------------------------------------------------------------------------- 1 | --- 2 | Download Help Link: 3 | Help Version: 5.0.0.0 4 | Locale: en-US 5 | Module Guid: 03F356FB-485D-41BC-B7FE-7F573ECC186F 6 | Module Name: Microsoft.SystemCenter.ServiceManagementAutomation 7 | --- 8 | 9 | # Microsoft.SystemCenter.ServiceManagementAutomation Module 10 | ## Description 11 | 12 | ## Microsoft.SystemCenter.ServiceManagementAutomation Cmdlets 13 | ### [Edit-SmaRunbook](Edit-SmaRunbook.md) 14 | Updates the draft field of a runbook. 15 | 16 | ### [Get-SmaAdminConfiguration](Get-SmaAdminConfiguration.md) 17 | Gets the SMA administration configuration. 18 | 19 | ### [Get-SmaCertificate](Get-SmaCertificate.md) 20 | Gets a certificate. 21 | 22 | ### [Get-SmaConnection](Get-SmaConnection.md) 23 | Gets an SMA connection. 24 | 25 | ### [Get-SmaConnectionField](Get-SmaConnectionField.md) 26 | Gets an SMA connection field. 27 | 28 | ### [Get-SmaConnectionType](Get-SmaConnectionType.md) 29 | Gets all SMA connection types. 30 | 31 | ### [Get-SmaCredential](Get-SmaCredential.md) 32 | Gets SMA credentials. 33 | 34 | ### [Get-SmaJob](Get-SmaJob.md) 35 | Gets a runbook job. 36 | 37 | ### [Get-SmaJobOutput](Get-SmaJobOutput.md) 38 | Gets the output of a SMA job. 39 | 40 | ### [Get-SmaLicense](Get-SmaLicense.md) 41 | Gets an SMA license. 42 | 43 | ### [Get-SmaModule](Get-SmaModule.md) 44 | Gets a module from SMA. 45 | 46 | ### [Get-SmaRunbook](Get-SmaRunbook.md) 47 | Gets an SMA runbook. 48 | 49 | ### [Get-SmaRunbookDefinition](Get-SmaRunbookDefinition.md) 50 | Gets a runbook definition. 51 | 52 | ### [Get-SmaRunbookWorkerDeployment](Get-SmaRunbookWorkerDeployment.md) 53 | Gets all runbook workers in the SMA deployment. 54 | 55 | ### [Get-SmaSchedule](Get-SmaSchedule.md) 56 | Gets an SMA schedule. 57 | 58 | ### [Get-SmaVariable](Get-SmaVariable.md) 59 | Gets an SMA variable. 60 | 61 | ### [Import-SmaModule](Import-SmaModule.md) 62 | Imports a module into SMA. 63 | 64 | ### [Import-SmaRunbook](Import-SmaRunbook.md) 65 | Imports a runbook into SMA. 66 | 67 | ### [New-SmaConnection](New-SmaConnection.md) 68 | Creates a connection instance in SMA. 69 | 70 | ### [New-SmaRunbookWorkerDeployment](New-SmaRunbookWorkerDeployment.md) 71 | Changes the runbook worker deployment in SMA. 72 | 73 | ### [Publish-SmaRunbook](Publish-SmaRunbook.md) 74 | Publishes a runbook in SMA. 75 | 76 | ### [Remove-SmaCertificate](Remove-SmaCertificate.md) 77 | Deletes a certificate from SMA. 78 | 79 | ### [Remove-SmaConnection](Remove-SmaConnection.md) 80 | Deletes a connection from SMA. 81 | 82 | ### [Remove-SmaCredential](Remove-SmaCredential.md) 83 | Deletes a credential from SMA. 84 | 85 | ### [Remove-SmaModule](Remove-SmaModule.md) 86 | Deletes a module from SMA. 87 | 88 | ### [Remove-SmaRunbook](Remove-SmaRunbook.md) 89 | Removes a runbook from SMA. 90 | 91 | ### [Remove-SmaSchedule](Remove-SmaSchedule.md) 92 | Deletes a schedule from SMA. 93 | 94 | ### [Remove-SmaVariable](Remove-SmaVariable.md) 95 | Deletes a variable from SMA. 96 | 97 | ### [Resume-SmaJob](Resume-SmaJob.md) 98 | Resumes a SMA job. 99 | 100 | ### [Set-SmaAdminConfiguration](Set-SmaAdminConfiguration.md) 101 | Modifies administrative configuration settings in SMA. 102 | 103 | ### [Set-SmaCertificate](Set-SmaCertificate.md) 104 | Creates or updates a certificate in SMA. 105 | 106 | ### [Set-SmaConnectionFieldValue](Set-SmaConnectionFieldValue.md) 107 | Modifies a connection field value for a connection object in SMA. 108 | 109 | ### [Set-SmaCredential](Set-SmaCredential.md) 110 | Creates or updates a credential in SMA. 111 | 112 | ### [Set-SmaLicense](Set-SmaLicense.md) 113 | Updates the SMA product key. 114 | 115 | ### [Set-SmaRunbookConfiguration](Set-SmaRunbookConfiguration.md) 116 | Modifies the configuration of a runbook. 117 | 118 | ### [Set-SmaSchedule](Set-SmaSchedule.md) 119 | Creates or modifies a schedule in SMA. 120 | 121 | ### [Set-SmaVariable](Set-SmaVariable.md) 122 | Creates or modifies a variable in SMA. 123 | 124 | ### [Start-SmaRunbook](Start-SmaRunbook.md) 125 | Starts a runbook job. 126 | 127 | ### [Stop-SmaJob](Stop-SmaJob.md) 128 | Stops a SMA job. 129 | 130 | ### [Suspend-SmaJob](Suspend-SmaJob.md) 131 | Suspends an SMA job. 132 | --------------------------------------------------------------------------------