{"id":1660,"date":"2013-06-25T00:00:00","date_gmt":"2013-06-25T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/how-to-document-your-powershell-library\/"},"modified":"2017-09-26T11:12:51","modified_gmt":"2017-09-26T11:12:51","slug":"how-to-document-your-powershell-library","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/sysadmin\/powershell\/how-to-document-your-powershell-library\/","title":{"rendered":"How To Document Your PowerShell Library"},"content":{"rendered":"<div class=\"article-content\">\n<ul class=\"series-articles\">\n<li class=\"series-articles--active\">Part 1: How to Document your PowerShell Library<\/li>\n<li>Part 2: <a href=\"https:\/\/www.simple-talk.com\/dotnet\/development\/using-c-to-create-powershell-cmdlets-the-basics\/\">Using C# to Create PowerShell Cmdlets: The Basics<\/a><\/li>\n<li>Part 3: <a href=\"https:\/\/www.simple-talk.com\/dotnet\/software-tools\/documenting-your-powershell-binary-cmdlets\/\">Documenting Your PowerShell Binary Cmdlets<\/a><\/li>\n<li>Part 4: <a href=\"https:\/\/www.simple-talk.com\/sysadmin\/powershell\/unified-approach-to-generating-documentation-for-powershell-cmdlets\/\">Unified Approach to Generating Documentation for PowerShell Cmdlets<\/a><\/li>\n<\/ul>\n<h2>Contents<\/h2>\n<ul>\n<li><a href=\"#one\">How to Document Your PowerShell Library<\/a>\n<ul>\n<li><a href=\"#two\">Test Driving the Generator<\/a><\/li>\n<li><a href=\"#three\">Controlling Output with the Page Template<\/a>\n<ul>\n<li><a href=\"#four\">Global Properties<\/a><\/li>\n<li><a href=\"#five\">Module Properties<\/a><\/li>\n<li><a href=\"#six\">Conditional Properties<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#seven\">Specifying Links<\/a><\/li>\n<li><a href=\"#eight\">Other Structural Considerations<\/a><\/li>\n<li><a href=\"#nine\">Other Stylistic Considerations<\/a><\/li>\n<li><a href=\"#ten\">Now Just Add Water<\/a><\/li>\n<li><a href=\"#eleven\">Conclusion<\/a><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><a id=\"one\"><\/a><\/p>\n<p class=\"start\">I am passionate about documentation. When you write library code-code for you or others to consume in other projects-it is almost useless without proper documentation. Most modern languages provide some level of support to assist in creating your documentation, where you instrument your code with documentation comments (<em>doc-comments<\/em> for short) that may then be automatically extracted and formatted to generate some documentation. But documenting individual methods or classes or files is <em>not<\/em> the same as documenting an API. Java has <a href=\"http:\/\/www.oracle.com\/technetwork\/java\/javase\/documentation\/index-jsp-135444.html\">javadoc<\/a> and C# has <a href=\"http:\/\/shfb.codeplex.com\/\">Sandcastle<\/a>, both of which generate a complete, cross-linked API documentation set. On the other hand, languages like Perl with <a href=\"http:\/\/perldoc.perl.org\/perldoc.html\">perldoc<\/a> and PowerShell with <a href=\"http:\/\/technet.microsoft.com\/library\/hh849696.aspx\">Get-Help<\/a>, generate only isolated module documentation, which I found rather unsatisfactory. So over the years I created an API-level documentation generator for Perl, later for T-SQL, and most recently for PowerShell. (Note that the Perl version <a href=\"http:\/\/cleancode.sourceforge.net\/wwwdoc\/software\/Pod2HtmlTree.html\">Pod2HtmlTree<\/a> is Perl-specific while the T-SQL one uses my generic XML conversion tool <a href=\"http:\/\/cleancode.sourceforge.net\/wwwdoc\/software\/XmlTransform.html\">XmlTransform<\/a> configured to handle SQL documentation, described in <a href=\"http:\/\/www.devx.com\/dbzone\/Article\/36646\">Add Custom XML Documentation Capability To Your SQL Code<\/a>.) The PowerShell generator (written in PowerShell, of course!) is a function in my <a href=\"https:\/\/github.com\/msorens\/DocTreeGenerator\">DocTreeGenerator<\/a> module called <strong>Convert-HelpToHtmlTree<\/strong>. (You can find my entire API bookshelf <a href=\"http:\/\/cleancode.sourceforge.net\/wwwdoc\/APIbookshelf.html\">here<\/a> and download libraries <a href=\"http:\/\/cleancode.sourceforge.net\/wwwdoc\/download.html\">here<\/a>.)<\/p>\n<p>This article discusses how to generate a complete API for a PowerShell library. Once you have instrumented your modules with doc-comments to satisfy <strong>Get-Help<\/strong>, you need surprisingly little extra work to supply to <strong>Convert-HelpToHtmlTree<\/strong>. But let&#8217;s start with the assumption, hardly realistic, I&#8217;m sure, that you do not have <em>any<\/em> doc-comments in your code and see what you can already do.<\/p>\n<p><a id=\"two\"><\/a><\/p>\n<h2>Test Driving the Generator<\/h2>\n<p>To help you visualize what <strong>Convert<\/strong><strong>-HelpToHtmlTree<\/strong> does, consider this simple example. Assume you have just one module, MathStuff.psm1 containing two functions:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">function Get-Max($itemList)\r\n{\r\n   ($itemList | measure-object -Maximum).Maximum\r\n}\r\n\r\nfunction Get-Min($itemList)\r\n{\r\n    ($itemList | measure-object -Minimum).Minimum\r\n}\r\n\r\nExport-ModuleMember Get-Max\r\nExport-ModuleMember Get-Min<\/pre>\n<p>Notice that there is as yet no documentation-comments in the code to document these functions. Yet PowerShell&#8217;s standard help cmdlet, <strong>Get-Help<\/strong><strong>,<\/strong> can still give you some information:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">[373]: Get-Help Get-Max -full\r\n\r\nNAME\r\n  Get-Max\r\n\r\nSYNTAX\r\n  Get-Max [[-itemList] &lt;Object&gt;]\r\n\r\nPARAMETERS\r\n  -itemList &lt;Object&gt;\r\n\r\n     Required?                   false\r\n     Position?                   0\r\n     Accept pipeline input?      false\r\n     Parameter set name          (All)\r\n     Aliases                     None\r\n     Dynamic?                    false\r\n\r\nINPUTS\r\n  None\r\n\r\nOUTPUTS\r\n  System.Object\r\n\r\nALIASES\r\n  None\r\n\r\nREMARKS\r\n  None<\/pre>\n<p>Similarly, <strong>Convert-HelpToHtmlTree<\/strong> can give you all the same basic information about the individual method-but it also builds an entire web framework around the namespace and module that contains it. Here is a snapshot of the input directory tree. By PowerShell convention, store your user modules under WindowsPowerShell\\Modules in your user directory. Under that path follows the namespace for this example, DemoModules. There are two module directories, <strong>MathStuff <\/strong>and<strong> StringUtilities<\/strong>; the former defines the two functions shown earlier, while the latter is empty.<\/p>\n<p>&nbsp;<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">[356]: ls .\\DemoModules -Recurse | % { $_.FullName }\r\nWindowsPowerShell\\Modules\\DemoModules\\MathStuff\r\nWindowsPowerShell\\Modules\\DemoModules\\StringUtilities\r\nWindowsPowerShell\\Modules\\DemoModules\\MathStuff\\MathStuff.psm1<\/pre>\n<p>Now execute <strong>Convert-HelpToHtmlTree<\/strong> with minimal arguments: the namespace to document and where to put it. <strong>Convert-HelpToHtmlTree<\/strong> needs more infrastructure to give you more useful output; it lets you know what you need to provide and where it goes:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Convert-HelpToHtmlTree -Namespaces DemoModules -TargetDir MyApi\r\nTarget dir: MyApi\r\nNamespace: DemoModules\r\n   Module: MathStuff\r\n      Command: Get-Max\r\n      Command: Get-Min\r\n** Missing summary (from DemoModules\\MathStuff\\module_overview.html)\r\n** Missing description (from DemoModules\\MathStuff\\MathStuff.psd1)\r\n   Module: StringUtilities\r\n** No objects found\r\nGenerating home page...\r\n** Missing summary (from DemoModules\\namespace_overview.html)\r\nGenerating contents page...\r\nDone: 1 namespace(s), 2 module(s), 2 function(s), 5 file(s) processed.<\/pre>\n<p>Here is the generated output tree:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">[353]: ls .\\MyApi -Recurse | % { $_.FullName }\r\nMyApi\\DemoModules\r\nMyApi\\contents.html\r\nMyApi\\index.html\r\nMyApi\\DemoModules\\MathStuff\r\nMyApi\\DemoModules\\MathStuff\\Get-Max.html\r\nMyApi\\DemoModules\\MathStuff\\Get-Min.html\r\nMyApi\\DemoModules\\MathStuff\\index.html<\/pre>\n<p>As is typical for a website, index.html is the entry point, displaying the <strong>namespace contents<\/strong>: a list of all contained modules with a brief description and a link to each module&#8217;s documentation (see Figure 1). In this case there is just one module, MathStuff, because StringUtilities mentioned above was empty.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1826-initial_web_home-69ae35e6-0a1b-48df-b665-d5835e301230.png\" alt=\"1826-initial_web_home-69ae35e6-0a1b-48df\" \/><\/p>\n<p class=\"caption\">Figure 1 Sample namespaces page: itemizes the modules for each namespace you have specified to document. (This is your top-level, home, or entry page.)<\/p>\n<p>If you traverse the link to MathStuff, you get the <strong>module contents<\/strong>: a list of all contained functions, and filters with a brief description and a link to each one&#8217;s documentation (see Figure 2). Notice in each of these figures, by the way, that you have the same warning messages inline as you did at the command prompt; here you get to see them in context. Figure 2 shows one of the few instances where <strong>Convert-HelpToHtmlTree<\/strong> fills in a slot where information is not available instead of giving a warning-in the table of methods it lists the syntax of a method in the absence of a textual description.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1826-initial_web_group-ceca7164-611e-4a0b-9751-bf3a4ce86220.png\" alt=\"1826-initial_web_group-ceca7164-611e-4a0\" \/><\/p>\n<p class=\"caption\">Figure 2 Sample module page: itemizes the functions, filters, and aliases for the current module.<\/p>\n<p>If you traverse the link to an individual function, you get the spruced-up output of <strong>Get-Help<\/strong> for that function(see Figure 3). This is exactly the same output that you get from <strong>Get-Help<\/strong>, but with these enhancements:<\/p>\n<ul>\n<li>It adds section headings to each of the main sections.<\/li>\n<li>It outputs most text in a proportional font.<\/li>\n<li>It outputs portions of text you designate (via leading white space on a line) in a fixed-width font.<\/li>\n<li>It preserves your line breaks, so if you want text to flow and wrap automatically at the window edge you must put it all in a single paragraph in your source file.<\/li>\n<li>It highlights the initial code sample in each example. (Note that, just like <strong>Get-Help<\/strong>, ONLY the first line immediately following the .EXAMPLE directive is treated as the code sample, so resist the urge to put line breaks in your sample!)<\/li>\n<li>Items designated as links are turned into active hyperlinks; these may be in any of seven different formats explained later.<\/li>\n<\/ul>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1826-initial_web_method-dd51125c-6d5e-473f-8d67-88b626b01f09.png\" alt=\"1826-initial_web_method-dd51125c-6d5e-47\" \/><\/p>\n<p class=\"caption\">Figure 3 Sample function page: provides help text for the current function or filter. This is a stylized version of the output of Get-Help.<\/p>\n<p>Finally, in the root folder of the output tree is contents.html, a table of contents, or index, if you will of everything (Figure 4).<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1826-initial_web_contents-46408bcf-0562-43c4-acf2-69f3d0a98c7c.png\" alt=\"1826-initial_web_contents-46408bcf-0562-\" \/><\/p>\n<p class=\"caption\">Figure 4 Sample master index\/contents page: an index of all documented objects; this page is accessible via a hyperlink on the top-level namespaces page.<\/p>\n<p><a id=\"three\"><\/a><\/p>\n<h2>Controlling Output with the Page Template<\/h2>\n<p>Figures 1 through 4 show the four page-types that <strong>Convert-<\/strong><strong>HelpToHtmlTree<\/strong> can generate. All four of these are derived from a single HTML template file. Take a look at the default template (psdoc_template.html) and you will find it sprinkled with place-holders for different properties. In the previous figures you will see some remnants where some place-holders have <em>not<\/em> been supplied, e.g. the empty copyright and revision dates at the bottom of each page.<\/p>\n<p>Here is the content of the <strong>&lt;body&gt;<\/strong> element of the template (the <strong>&lt;head&gt;<\/strong> element has been omitted for brevity). I have distinguished the global properties, module properties, and conditional properties with different colors for clarity. The following sections describe each of these three property types in detail.<\/p>\n<pre class=\"lang:xhtml theme:github\">&lt;body&gt;\r\n &lt;p class=\"subtitle\"&gt;{subtitle}{breadcrumbs}&lt;\/p&gt;\r\n  &lt;h1&gt;{title}&lt;\/h1&gt;\r\n  {preamble}\r\n  {ifdef home}\r\n  &lt;div&gt;\r\n  This is the home page.\r\n  See also: &lt;a href=\"contents.html\"&gt;Alphabetical Index&lt;\/a&gt;\r\n  &lt;\/div&gt;\r\n  {endif home}\r\n  {ifdef contents}\r\n  &lt;strong&gt;An alphabetical index to all namespaces, modules, and functions.&lt;\/strong&gt;\r\n  &lt;a href=\"index.html\"&gt;PowerShell API Home&lt;\/a&gt;\r\n  {endif contents}\r\n  {body}\r\n  {postscript}\r\n  {ifdef module}\r\n  This text appears only on the module index pages...\r\n  You may interpolate any standard module properties here, e.g.\r\n  &lt;ul&gt;\r\n  &lt;li&gt;GUID = {module.guid}&lt;\/li&gt;\r\n  &lt;li&gt;Version = {module.version}&lt;\/li&gt;\r\n  &lt;\/ul&gt;\r\n  {endif module}\r\n  {ifdef function}\r\n  This text appears only on the function pages...\r\n  You may interpolate any standard properties of the parent module here, e.g.\r\n  &lt;br&gt;\r\n  Version is {module.version}\r\n  {endif function}\r\n  &lt;p&gt;Copyright &amp;copy; {copyright}, Revised {revdate}&lt;\/p&gt;\r\n&lt;\/body&gt;<\/pre>\n<p><a id=\"four\"><\/a><\/p>\n<h3>Global Properties<\/h3>\n<p>Global properties are distinguished by having a single word in brackets for the place holder.<\/p>\n<div>\n<table>\n<tbody>\n<tr>\n<td>Place Holder<\/td>\n<td>Source<\/td>\n<td>Applies to PageType<\/td>\n<\/tr>\n<tr>\n<td><strong>{title}<\/strong><\/td>\n<td>current namespace + <strong>DocTitle<\/strong> parameter<\/td>\n<td>master, contents<\/td>\n<\/tr>\n<tr>\n<td><strong>{subtitle}<\/strong><\/td>\n<td>current namespace + <strong>DocTitle<\/strong> parameter<\/td>\n<td>module, function<\/td>\n<\/tr>\n<tr>\n<td><strong>{breadcrumbs}<\/strong><\/td>\n<td>generated relative paths<\/td>\n<td>module, function<\/td>\n<\/tr>\n<tr>\n<td><strong>{preamble}<\/strong><\/td>\n<td>manifest.Description property + module_overview.html &lt;body&gt; contents<\/td>\n<td>module<\/td>\n<\/tr>\n<tr>\n<td rowspan=\"4\"><strong>{body}<\/strong><\/td>\n<td>contents of Get-Help<\/td>\n<td>function<\/td>\n<\/tr>\n<tr>\n<td>LIST(function name + Get-Help.Synopsis property)<\/td>\n<td>module<\/td>\n<\/tr>\n<tr>\n<td>LIST(Namespace_overview.html &lt;body&gt; contents <br \/>\n\u00a0\u00a0\u00a0+ <em>LIST<\/em>(module name + manifest.Description property))<\/td>\n<td>master<\/td>\n<\/tr>\n<tr>\n<td>generated index<\/td>\n<td>contents<\/td>\n<\/tr>\n<tr>\n<td><strong>{postscript}<\/strong><\/td>\n<td>&nbsp;<\/td>\n<td><em>-not currently used-<\/em><\/td>\n<\/tr>\n<tr>\n<td><strong>{copyright}<\/strong><\/td>\n<td><strong>Copyright<\/strong> parameter<\/td>\n<td><em>-ALL-<\/em><\/td>\n<\/tr>\n<tr>\n<td><strong>{revdate}<\/strong><\/td>\n<td><strong>RevisionDate<\/strong> parameter<\/td>\n<td><em>-ALL-<\/em><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p><a id=\"five\"><\/a><\/p>\n<h3>Module Properties<\/h3>\n<p>Module-specific properties are distinguished by the form <strong>{<\/strong><strong>module<\/strong><strong><em>.<\/em><\/strong><em>property<\/em><strong>}<\/strong> where <strong><em>property<\/em><\/strong> may be any of the standard properties of a module. You can see what these are with this command:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Get-Module | Get-Member -MemberType Properties\r\n<\/pre>\n<p>As an example, Figure 2 shows that the template uses the <strong>module.guid<\/strong> and <strong>module.version<\/strong> properties used.<\/p>\n<p><a id=\"six\"><\/a><\/p>\n<h3>Conditional Properties<\/h3>\n<p>Finally, you will also see conditional properties in the template of the form:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">{ifdef pagetype}\r\n. . .\r\n{endif pagetype}<\/pre>\n<p>&#8230;where <strong><em>pagetype<\/em><\/strong> may be any of the four types of pages created:<\/p>\n<ul>\n<li>the single master page (pagetype=<strong>master<\/strong>),<\/li>\n<li>the single master index\/contents page (pagetype=<strong>contents<\/strong>),<\/li>\n<li>the module index pages, one per module (pagetype=<strong>module<\/strong>), and<\/li>\n<li>the function pages, one per exported function (pagetype=<strong>function<\/strong>).<\/li>\n<\/ul>\n<p>The content of these conditional sections (which may be any valid HTML) is included only on the pages of the corresponding type, while the other conditional sections are suppressed. Note that the module-specific place-holders discussed earlier (e.g. {<strong>module<\/strong>.<em>property<\/em>}) may be used in both module pages and function pages, but not in the master or contents page.<\/p>\n<p><a id=\"seven\"><\/a><\/p>\n<h2>Specifying Links<\/h2>\n<p>Unlike the MSDN pages for the standard PowerShell library, output generated here creates <em>live<\/em> links in your references (.LINK) documentation section. There are seven input variations permitted:<\/p>\n<div>\n<table>\n<tbody>\n<tr>\n<td>Item<\/td>\n<td><strong>MSDN-defined (built-in) cmdlet<\/strong><\/td>\n<\/tr>\n<tr>\n<td>Sample Input<\/td>\n<td>Get-ChildItem<\/td>\n<\/tr>\n<tr>\n<td>Sample Output<\/td>\n<td>&lt;a href=&#8217;http:\/\/technet.microsoft.com\/en-us\/library\/dd347686.aspx&#8217;&gt;Get-ChildItem&lt;\/a&gt;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<div>\n<table>\n<tbody>\n<tr>\n<td>Item<\/td>\n<td><strong>MSDN-defined (built-in) topic<\/strong><\/td>\n<\/tr>\n<tr>\n<td>Sample Input<\/td>\n<td>about_Aliases<\/td>\n<\/tr>\n<tr>\n<td>Sample Output<\/td>\n<td>&lt;a href=&#8217;http:\/\/technet.microsoft.com\/en-us\/library\/dd347645.aspx&#8217;&gt;about_Aliases&lt;\/a&gt;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<div>\n<table>\n<tbody>\n<tr>\n<td>Item<\/td>\n<td><strong>Custom function in the same module<\/strong><\/td>\n<\/tr>\n<tr>\n<td>Sample Input<\/td>\n<td>New-CustomFunction<\/td>\n<\/tr>\n<tr>\n<td>Sample Output<\/td>\n<td>&lt;a href=&#8217;New-CustomFunction.html&#8217;&gt;New-CustomFunction&lt;\/a&gt;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<div>\n<table>\n<tbody>\n<tr>\n<td>Item<\/td>\n<td><strong>Custom function in a different local module<\/strong><\/td>\n<\/tr>\n<tr>\n<td>Sample Input<\/td>\n<td>New-FunctionInOtherModule<\/td>\n<\/tr>\n<tr>\n<td>Sample Output<\/td>\n<td>&lt;a href=&#8217;..\/..\/DemoModules\/MathStuff\/New-FunctionInOtherModule.html&#8217;&gt;New-FunctionInOtherModule&lt;\/a&gt;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<div>\n<table>\n<tbody>\n<tr>\n<td>Item<\/td>\n<td><strong>Plain text <\/strong><em>(for cases where you have no web resource to link to)<\/em><\/td>\n<\/tr>\n<tr>\n<td>Sample Input<\/td>\n<td>Butterflies and Dinosaurs&#8211;the missing link?<\/td>\n<\/tr>\n<tr>\n<td>Sample Output<\/td>\n<td>Butterflies and Dinosaurs&#8211;the missing link?<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<div>\n<table>\n<tbody>\n<tr>\n<td>Item<\/td>\n<td><strong>Explicit link with a label<\/strong><\/td>\n<\/tr>\n<tr>\n<td>Sample Input<\/td>\n<td>[other important stuff] (http:\/\/foobar.com)<\/td>\n<\/tr>\n<tr>\n<td>Sample Output<\/td>\n<td>&lt;a href=&#8217;http:\/\/foobar.com&#8217;&gt;other important stuff&lt;\/a&gt;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<div>\n<table>\n<tbody>\n<tr>\n<td>Item<\/td>\n<td><strong>Explicit link without a label<\/strong><\/td>\n<\/tr>\n<tr>\n<td>Sample Input<\/td>\n<td>http:\/\/alpha\/beta\/<\/td>\n<\/tr>\n<tr>\n<td>Sample Output<\/td>\n<td>&lt;a href=&#8217;http:\/\/alpha\/beta\/&#8217;&gt;http:\/\/alpha\/beta\/&lt;\/a&gt;&lt;\/<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>The MSDN references are automatically constructed by referencing two fixed MSDN reference pages, one for cmdlets and one for topics. If those fixed references ever change URLs, that will break the generator; you will need to update those URLs in the internal <strong>Get-CmdletDocLinks<\/strong> function to mend it. The input format for these was designed keeping in mind that while <strong>Convert-HelpToHtmlTree<\/strong> converts these to links, <strong>Get-Help<\/strong> does not-it will display the original, raw text.<\/p>\n<p><a id=\"eight\"><\/a><\/p>\n<h2>Other Structural Considerations<\/h2>\n<p>Besides the standard documentation comments that <strong>Get-Help<\/strong> would use to display your modules, <strong>Convert-HelpToHtmlTree<\/strong> needs some additional doc-comments to generate a cohesive API for you.<\/p>\n<ol>\n<li>Each <em>module<\/em> (x.psm1) must have an associated manifest (x.psd1) in the same directory and the manifest must include a <strong>Description<\/strong> property.<\/li>\n<li>Each <em>module<\/em> must have an associated overview (module_overview.html) in the same directory. This is a standard HTML file. The contents of the <strong>&lt;body&gt;<\/strong> element are extracted verbatim as the introductory text of the index.html page for each module.<\/li>\n<li>Each <em>namespace<\/em> must also include an associated overview (namespace_overview.html). This is a standard HTML file. The contents of the <strong>&lt;body&gt;<\/strong> element are extracted verbatim as the introductory text of each namespace in the master index.html page.<\/li>\n<\/ol>\n<p>By reviewing the error messages in the very beginning of the article as well as the table of global properties early on, you should now see how these pieces fit together.<\/p>\n<p>Note that I use the term <em>namespace<\/em> here informally because (as of v3) PowerShell does not currently have the notion of namespaces. <strong>Convert-HelpToHtmlTree<\/strong>, however, <em>requires<\/em> you to structure your modules grouped in namespaces. Thus, if you have a module MyStuff.psm1, normal PowerShell conventions require you to store this in a path like this:<\/p>\n<p>&#8230;\\WindowsPowerShell\\Modules\\<strong>MyStuff\\MyStuff.psm1<\/strong><\/p>\n<p>&#8230;but <strong>Convert-HelpToHtmlTree<\/strong> requires you to include one more level for namespace, so the module must be stored in a path like this:<\/p>\n<p>&#8230;\\WindowsPowerShell\\Modules\\<strong>MyNamespace\\MyStuff\\MyStuff.psm1<\/strong><\/p>\n<p>This allows you to organize your modules into more than one logical group if desired. In my own PowerShell library, for example, I have FileTools, SqlTools, and SvnTools modules (among others) all under the CleanCode namespace. But you may, however, include multiple namespaces. Here&#8217;s a sample input tree illustrating this:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">WindowsPowerShell\\Modules\r\n+---namespace1\r\n  +---namespace_overview.html\r\n  +---moduleA\r\n     +---module_overview.html\r\n     +---moduleA.psm1\r\n     +---moduleA.psd1\r\n     +---moduleB\r\n     +---module_overview.html\r\n     +---moduleB.psm1\r\n     +---moduleB.psd1\r\n  etc...\r\n+---namespace2\r\n  +---namespace_overview.html\r\n  +---moduleX\r\n     +---module_overview.html\r\n     +---moduleX.psm1\r\n     +---moduleX.psd1\r\n  +---moduleY\r\n     +---module_overview.html\r\n     +---moduleY.psm1\r\n     +---moduleY.psd1\r\n  etc...<\/pre>\n<p>The output structure mirrors the input structure; the above input might generate the output tree shown below. There is a single master index page documenting all namespaces.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$TargetDir\r\n+---contents.html\r\n+---index.html\r\n+---namespace1\r\n  +---moduleA\r\n     +---index.html\r\n     +---Function1.html\r\n     +---Function2.html\r\n     +---Function3.html\r\n     +---Function4.html\r\n     etc...\r\n  +---moduleB\r\n     +---index.html\r\n     +---Function1.html\r\n     +---Function2.html\r\n     etc...\r\n+---namespace2\r\n  +---moduleX\r\n     +---index.html\r\n     +---Function1.html\r\n     etc...\r\n  +---moduleY\r\n     +---index.html\r\n     +---Function1.html\r\n     +---Function2.html\r\n     etc...\r\netc...<\/pre>\n<p><a id=\"nine\"><\/a><\/p>\n<h2>Other Stylistic Considerations<\/h2>\n<p>You have seen how to customize the generated output with the template file and how to format items in your .LINKS section. There are just a few more considerations to be mindful of as you write your documentation comments for your modules, touched on earlier:<\/p>\n<ul>\n<li>Body text, by default, is output with a proportional font (your standard browser font) and flows\/wraps automatically at the window edge based on your paragraph boundaries-that is, where you have hard (explicit) carriage returns.<\/li>\n<li>By inference, if you want to output a list of items, you do not need to do anything special. Just type in the list with carriage returns and the output will mirror the input. It will still be in the standard browser font.<\/li>\n<li>If you want to include a code sample in a fixed-width font, or even if you just want to output a list of items in a fixed-width font, simply have leading whitespace on each line.<\/li>\n<li>The first line-and only the first line-following an .EXAMPLE section is special to <strong>Get-Help<\/strong>: it is considered your code sample. Thus, you really want to keep your code sample all on one line, no matter how long and ugly it gets. <strong>Convert-HelpToHtmlTree<\/strong> also considers the same first line a code sample and highlights it. So resist the urge to put line breaks in your sample!<\/li>\n<\/ul>\n<p><a id=\"ten\"><\/a><\/p>\n<h2>Now Just Add Water&#8230;<\/h2>\n<p>With the preliminaries out of the way, you are ready to put the generator to work. There are just a few parameters to specify on the command line. Here is essentially the script I use to generate the API for my own <a href=\"https:\/\/github.com\/msorens\/DocTreeGenerator\">CleanCode PowerShell library<\/a>:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Import-Module DocTreeGenerator\r\n\r\n$scriptDir = Split-Path $script:MyInvocation.MyCommand.Path\r\n\r\nConvert-HelpToHtmlTree `\r\n   -Namespaces CleanCode `\r\n   -TemplateName (Join-Path $scriptDir psdoc_template.html) `\r\n   -DocTitle 'PowerShell Libraries v1.2.01 API' `\r\n   -Copyright '2012' `\r\n   -RevisionDate '2012.12.10' `\r\n   -TargetDir c:\\usr\\tmp\\psdoc_tmp<\/pre>\n<p>This short script uses a custom template file located in the same directory as the above script. (If you omit specifying a template it uses the default template supplied. That template includes the CSS rules embedded in the template just to keep it all to a single file; in practice you should generally move the CSS rules to a separate file.)<\/p>\n<p>Figure 5 shows you the <a href=\"http:\/\/cleancode.sourceforge.net\/api\/powershell\/\">API home page<\/a> for my PowerShell libraries.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1826-cleancode_web_home-5cafa97c-4294-4594-9e3e-53462e26416e.jpg\" alt=\"1826-cleancode_web_home-5cafa97c-4294-45\" \/><\/p>\n<p class=\"caption\">Figure 5 Actual API home page for my CleanCode library of PowerShell functions.<\/p>\n<p>And Figure 6 shows a portion of its associated <a href=\"http:\/\/cleancode.sourceforge.net\/api\/powershell\/contents.html\">master index<\/a>. From either of those you can browse through the remainder of the documentation set.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1826-cleancode_web_contents-2e01d05e-3f53-4cab-84dd-31e125b499fa.jpg\" alt=\"1826-cleancode_web_contents-2e01d05e-3f5\" \/><\/p>\n<p class=\"caption\">Figure 6 Portion of the index (or table of contents) of the CleanCode library, indexing namespaces, modules, functions, and filters.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1826-side%20by%20side%201-d28aaeb5-2091-4784-872b-3b9f95b4f328.jpg\" alt=\"1826-side%20by%20side%201-d28aaeb5-2091-\" \/><\/p>\n<p class=\"caption\">Figure 7 Side by side comparison of the stylized web page output of Convert-HelpToHtmlTree (left) and Get-Help (right). Note the section headers standout for easy scanning. The description shows both normal, wrapped text as well as an example of preformatted text.<\/p>\n<p><a id=\"eleven\"><\/a><\/p>\n<h2>Conclusion<\/h2>\n<p>Some IDEs provide great support for adding doc-comments. Visual Studio, for example, with the GhostDoc add-on almost writes the doc-comments for you. Alas, PowerShell does not yet have such a &#8220;ghost writer&#8221; available. To do it yourself, start with <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/dd819489.aspx\">about_Comment_Based_Help<\/a> (which you can also access from the PowerShell prompt by feeding that to <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/dd347639.aspx\">Get-Help<\/a>!). Scroll down to the <strong>Syntax for Comment-Based Help in <\/strong><strong>Function<\/strong><strong>s<\/strong> section. Note that the page also talks about adding help for the script itself; that applies only to script files (<strong>ps1<\/strong> files); it does <em>not<\/em> apply to modules (<strong>psm1<\/strong> files). What you will see here is that you must add a special comment section that looks like this for each function (those that may appear more than once are shown repeated here):<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">&lt;#\r\n.SYNOPSIS\r\n.DESCRIPTION\r\n.PARAMETER x\r\n.PARAMETER y\r\n.INPUTS\r\n.OUTPUTS\r\n.EXAMPLE\r\n.EXAMPLE\r\n.EXAMPLE\r\n.EXAMPLE\r\n.LINK\r\n.LINK\r\n.NOTES\r\n#&gt;<\/pre>\n<p>After each keyword directive, you can have as much freeform text as you need. You will find all the keywords described in the subsequent section of <strong>about_Comment_Based_Help<\/strong> entitled <strong>Comment-Based Help Keywords<\/strong>.<\/p>\n<p>Now go forth and document!<\/p>\n<div class=\"note\">\n<p><strong>Update &#8211; April 2016<\/strong> <br \/>\nSince this article, I&#8217;ve created a wallchart putting both XmlDoc2Cmdlet and DocTreeGenerator in context, showing you how to do a complete documentation solution for your PowerShell work in both C# and PowerShell. <a href=\"https:\/\/www.simple-talk.com\/sysadmin\/powershell\/unified-approach-to-generating-documentation-for-powershell-cmdlets\/\">Click here for more details<\/a>. <a href=\"https:\/\/www.simple-talk.com\/sysadmin\/powershell\/unified-approach-to-generating-documentation-for-powershell-cmdlets\/\"><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/2407-1-5728ddc3-433a-4b82-a6dc-84f5f450b519.png\" alt=\"2407-1-5728ddc3-433a-4b82-a6dc-84f5f450b\" \/><\/a><\/p>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>PowerShell provides comment-based  help for functions and scripts with Get-Help, but when you want to document modules or generate a complete indexed API in HTML format, just as you can with Sandcastle for .NET or javadoc for Java, then stronger magic is required. Michael Sorens shows you how it is done, with source code.&hellip;<\/p>\n","protected":false},"author":221868,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[35],"tags":[4143,5874,4681,4635,4179,4871],"coauthors":[6802],"class_list":["post-1660","post","type-post","status-publish","format-standard","hentry","category-powershell","tag-net","tag-apis","tag-documentation","tag-powershell","tag-source-control","tag-sysadmin"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1660","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221868"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=1660"}],"version-history":[{"count":7,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1660\/revisions"}],"predecessor-version":[{"id":72940,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1660\/revisions\/72940"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=1660"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=1660"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=1660"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=1660"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}