Unpacking Software Livestream

Join our monthly Unpacking Software livestream to hear about the latest news, chat and opinion on packaging, software deployment and lifecycle management!

Learn More

Chocolatey Product Spotlight

Join the Chocolatey Team on our regular monthly stream where we put a spotlight on the most recent Chocolatey product releases. You'll have a chance to have your questions answered in a live Ask Me Anything format.

Learn More

Chocolatey Coding Livestream

Join us for the Chocolatey Coding Livestream, where members of our team dive into the heart of open source development by coding live on various Chocolatey projects. Tune in to witness real-time coding, ask questions, and gain insights into the world of package management. Don't miss this opportunity to engage with our team and contribute to the future of Chocolatey!

Learn More

Calling All Chocolatiers! Whipping Up Windows Automation with Chocolatey Central Management

Webinar from
Wednesday, 17 January 2024

We are delighted to announce the release of Chocolatey Central Management v0.12.0, featuring seamless Deployment Plan creation, time-saving duplications, insightful Group Details, an upgraded Dashboard, bug fixes, user interface polishing, and refined documentation. As an added bonus we'll have members of our Solutions Engineering team on-hand to dive into some interesting ways you can leverage the new features available!

Watch On-Demand
Chocolatey Community Coffee Break

Join the Chocolatey Team as we discuss all things Community, what we do, how you can get involved and answer your Chocolatey questions.

Watch The Replays
Chocolatey and Intune Overview

Webinar Replay from
Wednesday, 30 March 2022

At Chocolatey Software we strive for simple, and teaching others. Let us teach you just how simple it could be to keep your 3rd party applications updated across your devices, all with Intune!

Watch On-Demand
Chocolatey For Business. In Azure. In One Click.

Livestream from
Thursday, 9 June 2022

Join James and Josh to show you how you can get the Chocolatey For Business recommended infrastructure and workflow, created, in Azure, in around 20 minutes.

Watch On-Demand
The Future of Chocolatey CLI

Livestream from
Thursday, 04 August 2022

Join Paul and Gary to hear more about the plans for the Chocolatey CLI in the not so distant future. We'll talk about some cool new features, long term asks from Customers and Community and how you can get involved!

Watch On-Demand
Hacktoberfest Tuesdays 2022

Livestreams from
October 2022

For Hacktoberfest, Chocolatey ran a livestream every Tuesday! Re-watch Cory, James, Gary, and Rain as they share knowledge on how to contribute to open-source projects such as Chocolatey CLI.

Watch On-Demand

Downloads:

970

Downloads of v 0.2.0:

970

Last Update:

22 Nov 2016

Package Maintainer(s):

Software Author(s):

  • Aaron Jensen

Tags:

powershell module help tools

Silk

  • 1
  • 2
  • 3

0.2.0 | Updated: 22 Nov 2016

Downloads:

970

Downloads of v 0.2.0:

970

Maintainer(s):

Software Author(s):

  • Aaron Jensen

Silk 0.2.0

Legal Disclaimer: Neither this package nor Chocolatey Software, Inc. are affiliated with or endorsed by Aaron Jensen. The inclusion of Aaron Jensen trademark(s), if any, upon this webpage is solely to identify Aaron Jensen goods or services and not for commercial purposes.

  • 1
  • 2
  • 3

All Checks are Passing

3 Passing Tests


Validation Testing Passed


Verification Testing Passed

Details

Scan Testing Successful:

No detections found in any package files

Details
Learn More

Deployment Method: Individual Install, Upgrade, & Uninstall

To install Silk, run the following command from the command line or from PowerShell:

>

To upgrade Silk, run the following command from the command line or from PowerShell:

>

To uninstall Silk, run the following command from the command line or from PowerShell:

>

Deployment Method:

NOTE

This applies to both open source and commercial editions of Chocolatey.

1. Enter Your Internal Repository Url

(this should look similar to https://community.chocolatey.org/api/v2/)


2. Setup Your Environment

1. Ensure you are set for organizational deployment

Please see the organizational deployment guide

2. Get the package into your environment

  • Open Source or Commercial:
    • Proxy Repository - Create a proxy nuget repository on Nexus, Artifactory Pro, or a proxy Chocolatey repository on ProGet. Point your upstream to https://community.chocolatey.org/api/v2/. Packages cache on first access automatically. Make sure your choco clients are using your proxy repository as a source and NOT the default community repository. See source command for more information.
    • You can also just download the package and push it to a repository Download

3. Copy Your Script

choco upgrade silk -y --source="'INTERNAL REPO URL'" [other options]

See options you can pass to upgrade.

See best practices for scripting.

Add this to a PowerShell script or use a Batch script with tools and in places where you are calling directly to Chocolatey. If you are integrating, keep in mind enhanced exit codes.

If you do use a PowerShell script, use the following to ensure bad exit codes are shown as failures:


choco upgrade silk -y --source="'INTERNAL REPO URL'" 
$exitCode = $LASTEXITCODE

Write-Verbose "Exit code was $exitCode"
$validExitCodes = @(0, 1605, 1614, 1641, 3010)
if ($validExitCodes -contains $exitCode) {
  Exit 0
}

Exit $exitCode

- name: Install silk
  win_chocolatey:
    name: silk
    version: '0.2.0'
    source: INTERNAL REPO URL
    state: present

See docs at https://docs.ansible.com/ansible/latest/modules/win_chocolatey_module.html.


chocolatey_package 'silk' do
  action    :install
  source   'INTERNAL REPO URL'
  version  '0.2.0'
end

See docs at https://docs.chef.io/resource_chocolatey_package.html.


cChocoPackageInstaller silk
{
    Name     = "silk"
    Version  = "0.2.0"
    Source   = "INTERNAL REPO URL"
}

Requires cChoco DSC Resource. See docs at https://github.com/chocolatey/cChoco.


package { 'silk':
  ensure   => '0.2.0',
  provider => 'chocolatey',
  source   => 'INTERNAL REPO URL',
}

Requires Puppet Chocolatey Provider module. See docs at https://forge.puppet.com/puppetlabs/chocolatey.


4. If applicable - Chocolatey configuration/installation

See infrastructure management matrix for Chocolatey configuration elements and examples.

Package Approved

This package was approved by moderator flcdrg on 26 Nov 2016.

Description

PowerShell module for publishing another module's help system as a website.


LICENSE
 
NOTICE
 
RELEASE_NOTES.md
# 0.2.0 (21 November 2016)

 * Improved `about_Silk` help topic.
 * Added example scripts `Invoke-Build.ps1`, `Publish-Module.ps1`, and `New-Website.ps1` to show how to use Silk.
Silk\bin\MarkdownSharp.dll
md5: 6A2AA9E5FD90BC15B51CA32113271B34 | sha1: 36A6C7FFF88DED2F28E846320721BC2A758B9C29 | sha256: 605E6AD93DFEA7F777735558A01B286602C156D1062E7A5872F84410128E1B92 | sha512: 6037A6FCFBB13955E9554BDA72522A1F9406132A2D8986C71CA94724391525610E454DA4F5757E81A0DB57FAE75C0363EC47C69BD9BD00BDBB4FF91CF13CE6EA
Silk\bin\NuGet.exe
md5: C0C47419C0641F4638E3758F4E997C2B | sha1: E412DEA7B234D73048213AB9391500501DEB46CD | sha256: 8339DEB284895BC2D8B41C762C9D967DD8A62E420E46B877457A4361DCF42068 | sha512: ADB5EE24A22B9E3C0C35E4C8C8332B1E55F87AF0232BD4AF3D72F4419FB9F33AB6456ED31965BD0FCF980C85F5CBE61EEA3B4442A135B603D93406B97204C0AE
Silk\en-US\about_Silk.help.txt
NAME
    Silk
    
SYNOPSIS
    Silk is a PowerShell module for authoring, building, and publishing
    PowerShell modules and creating a website from a module's help 
    topics.

DESCRIPTION

    Silk is a PowerShell module for PowerShell module authors. Use it to:

    * Automatically version of your module
    * Easily build your module, including any compiled assemblies
    * Publish your module as a Chocolatey and/or NuGet package
    * Publish your module to the PowerShell gallery
    * Convert your module's help as a website

    If you look in Silk's Examples directory, you'll see three scripts 
    that demonstrate its capabilities:

    * `Invoke-Build.ps1` shows how to build and package your module with 
    Silk
    * `Publish-Module.ps1` shows how to publish your module to 
    Chocolatey, NuGet, and the PowerShell Gallery.
    * `Publish-Website.ps1` shows how to publish your module's help to a 
    website.


    ## Versioning and Building

    To get started, create a `build.ps1` file in the root of your 
    repository. In your `build.ps1` file, import Silk. Use Silk's 
    `Set-ModuleVersion` function to set your module's version and 
    compile any code.

        Set-ModuleVersion -ManifestPath "PATH TO MODULE'S *.psd1 FILE" `
                          -Version "VERSION TO BUILD" `
                          -ReleaseNotesPath 'PATH TO RELEASE NOTES FILE' 
                          
    `Set-ModuleVersion` will also set the version in a .nuspec file (for 
    creating Chocolatey and NuGet packages) and an AssemblyInfo.cs file 
    (for versioning your module's assembly. 

    `Set-ModuleVersion` will build a Visual Studio solution if you pass 
    its path via the `SolutionPath` parameter.

    Silk expects your release notes files to be written in Markdown. It 
    expects each level-1 header (marked with a single `#` at the 
    beginning of a line) to be a version number, and everything after 
    that header to the next level-1 header (or the end of the file) to 
    be the release notes for that version. When `Set-ModuleVersion` sets 
    the version in a release notes file, it only sets the first level-1 
    header it finds.

    See the `RELEASE_NOTES.md` included with the Silk module for an 
    example.


    # Packaging and Preparing to Release

    Silk can do the following actions to prepare your module for a 
    release:

    * The `Set-ReleaseNotesReleaseDate` function set the release date in 
    your release notes file.
    * The `Set-ModuleManifestMetadata` function sets the release notes 
    and tags fields in your module manifest (i.e. .psd1 file). 
    * Thge `Set-ModuleNuspec` function sets metadata in a .nuspec file 
    (for Chocolatey and NuGet packages). The .nuspec file's description, 
    version, tags, and copyright fields are set from your module's 
    manifest (i.e. .psd1) file. The .nuspec file's releaseNotes property 
    is pulled from your release notes file.
    * The `New-NugetPackage` function will create a NuGet package from a 
    .nuspec file.
    * The `New-ChocolateyPackage` function will create a Chocolatey 
    package from a .nuspec file. 

    ## Publishing/Releasing a Module

    Silk has the following functions you can use to publish/release your 
    module:

    * `Publish-NuGetPackage` will publish your module's .nupkg file to 
    nuget.org. (You create a .nupkg file from a .nuspec file with the 
    `New-NuGetPackage` function.)
    * `Pulish-ChocolateyPackage` will publish your module's .nupkg file 
    to chocolatey.org. (You create a .nupkg file from a .nuspec file 
    with the `New-ChocolateyPackage` function.
    * `Publish-PowerShellGalleryModule` will publish your module to the 
    PowerShell Gallery (https://powershellgallery.com).

    ## Creating a Website from a Module's Help Topics

    Silk has the following functions for generating a website from a 
    module's help topics. Silk assumes your help is written in Markdown.

    Silk will auto-link to commands inside your module. Surround your 
    module's command names, script names, and about topics in backticks, 
    and Silk will convert it to a link to that item's help topic. For 
    example, `about_Silk` will get converted to `<a 
    href="about_Silk.html">about_Silk</a>`.

    * `Convert-ModuleHelpToHtml` converts the help for each command in a 
    module to an .html page/file.
    * `New-ModuleHelpIndex` creates an index.html page that includes 
    links to a module's `about_` help topics, standalone scripts, and 
    commands. The commands are organized into three tabs: one organized 
    by tags, one organized alphabetically by command name, and one 
    organized alphabetically by verb.
    * `Convert-ABoutTopicToHtml` converts about topics to HTML pages.

Silk\Examples\Invoke-Build.ps1
<#
.SYNOPSIS
Sets the version number for the LibGit2 module.
#>
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#     http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
[CmdletBinding()]
param(
    [Version]
    # The version to build. If not supplied, build the version as currently defined.
    $Version,

    [Switch]
    # Build and create packages that will be published.
    $ForRelease
)

#Requires -Version 4
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

& (Join-Path -Path $PSScriptRoot -ChildPath 'Silk\Import-Silk.ps1' -Resolve)

$manifestPath = Join-Path -Path $PSScriptRoot -ChildPath 'Silk\Silk.psd1'

$manifest = Test-ModuleManifest -Path $manifestPath
if( -not $manifest )
{
    return
}

$nuspecPath = Join-Path -Path $PSScriptRoot -ChildPath 'Silk.nuspec' -Resolve
$releaseNotesPath = Join-Path -Path $PSScriptRoot -ChildPath 'RELEASE_NOTES.md' -Resolve

# If you need to compile an assembly or other code, add SolutionPath and AssemblyInfoPath parameters, e.g.
#                 -SolutionPath (Join-Path -Path $PSScriptRoot -ChildPath 'Source\Silk.sln' -Resolve) `
#                 -AssemblyInfoPath (Join-Path -Path $PSScriptRoot -ChildPath 'Source\Silk\Properties\AssemblyInfo.cs' -Resolve)
Set-ModuleVersion -ManifestPath $manifestPath `
                  -Version $Version `
                  -ReleaseNotesPath $releaseNotesPath `
                  -NuspecPath $nuspecPath

Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath 'Tools\Pester' -Resolve)

$result = Invoke-Pester -Script (Join-Path -Path $PSScriptRoot -ChildPath 'Tests') -PassThru
if( $result.FailedCount )
{
    exit
}

if( -not $ForRelease )
{
    return
}

$valid = Assert-ModuleVersion -ManifestPath $manifestPath -ReleaseNotesPath $releaseNotesPath -NuspecPath $nuspecPath -ExcludeAssembly 'LibGit2Sharp.dll'
if( -not $valid )
{
    Write-Error -Message ('Silk isn''t at the right version. Please use the -Version parameter to set the version.')
    return
}

Set-ReleaseNotesReleaseDate -ManifestPath $manifestPath -ReleaseNotesPath $releaseNotesPath

$tags = @( 'powershell', 'module', 'help', 'tools' )

Set-ModuleManifestMetadata -ManifestPath $manifestPath -Tag $tags -ReleaseNotesPath $releaseNotesPath

$outputDirectory = Join-Path -Path $PSScriptRoot -ChildPath 'Output'
if( (Test-Path -Path $outputDirectory -PathType Container) )
{
    Get-ChildItem -Path $outputDirectory | Remove-Item -Recurse
}
else
{
    New-Item -Path $outputDirectory -ItemType 'directory'
}

Set-ModuleNuspec -ManifestPath $manifestPath `
                 -NuspecPath $nuspecPath `
                 -ReleaseNotesPath $releaseNotesPath `
                 -Tags $tags `
                 -PackageID 'Silk.PowerShell' `
                 -PackageTitle 'Silk.PowerShell'


New-NuGetPackage -OutputDirectory (Join-Path -Path $outputDirectory -ChildPath 'nuget.org') `
                 -ManifestPath $manifestPath `
                 -NuspecPath $nuspecPath `
                 -NuspecBasePath $PSScriptRoot `
                 -PackageName 'Silk.PowerShell'

Set-ModuleNuspec -ManifestPath $manifestPath `
                 -NuspecPath $nuspecPath `
                 -ReleaseNotesPath $releaseNotesPath `
                 -Tags $tags `
                 -PackageID 'Silk' `
                 -PackageTitle 'Silk'

New-ChocolateyPackage -OutputDirectory (Join-Path -Path $outputDirectory -ChildPath 'chocolatey.org') `
                      -ManifestPath $manifestPath `
                      -NuspecPath $nuspecPath

$source = Join-Path -Path $PSScriptRoot -ChildPath 'Silk'
$destination = Join-Path -Path $outputDirectory -ChildPath 'Silk'
robocopy.exe $source $destination /MIR /NJH /NJS /NP /NDL /XD /XF '*.pdb'

$examplesDir = Join-Path -Path $destination -ChildPath 'Examples'
New-Item -Path $examplesDir -ItemType 'Directory'

Get-ChildItem -Path (Join-Path -Path $PSScriptRoot -ChildPath 'build.ps1') |
    Copy-Item -Destination (Join-Path -Path $examplesDir -ChildPath 'Invoke-Build.ps1')

Get-ChildItem -Path (Join-Path -Path $PSScriptRoot -ChildPath 'New-Website.ps1') |
    Copy-Item -Destination $examplesDir

Get-ChildItem -Path (Join-Path -Path $PSScriptRoot -ChildPath 'Publish-Silk.ps1') |
    Copy-Item -Destination (Join-Path -Path $examplesDir -ChildPath 'Publish-Module.ps1')

Get-ChildItem -Path 'RELEASE_NOTES.md','LICENSE','NOTICE' | Copy-Item -Destination $destination
Silk\Examples\New-Website.ps1
<#
.SYNOPSIS
Creates the get-libgit2.org website.

.DESCRIPTION
The `New-Website.ps1` script generates the get-libgit2.org website. It uses the Silk module for Markdown to HTML conversion.
#>

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#     http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

[CmdletBinding()]
param(
)

#Requires -Version 4
Set-StrictMode -Version 'Latest'

function Out-HtmlPage
{
    param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [Alias('Html')]
        # The contents of the page.
        $Content,

        [Parameter(Mandatory=$true)]
        # The title of the page.
        $Title,

        [Parameter(Mandatory=$true)]
        # The path under the web root of the page.
        $VirtualPath
    )

    begin
    {
        Set-StrictMode -Version 'Latest'
    }

    process
    {

        $webRoot = Join-Path -Path $PSScriptRoot -ChildPath 'get-silk.org'
        $path = Join-Path -Path $webRoot -ChildPath $VirtualPath
        $templateArgs = @(
                            $Title,
                            $Content,
                            (Get-Date).Year
                        )
        @'
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>{0}</title>
    <link href="silk.css" type="text/css" rel="stylesheet" />
	<link href="styles.css" type="text/css" rel="stylesheet" />
</head>
<body>

    <ul id="SiteNav">
		<li><a href="index.html">Get-Silk</a></li>
		<li><a href="documentation.html">-Documentation</a></li>
        <li><a href="releasenotes.html">-ReleaseNotes</a></li>
		<li><a href="http://pshdo.com">-Blog</a></li>
        <li><a href="http://github.com/pshdo/Silk">-Project</a></li>
    </ul>

    {1}

	<div class="Footer">
		Copyright {2} <a href="http://pshdo.com">Aaron Jensen</a>.
	</div>

</body>
</html>
'@ -f $templateArgs | Set-Content -Path $path
    }

    end
    {
    }
}

$silkRoot = Join-Path -Path $PSScriptRoot -ChildPath '.\Silk' -Resolve

if( (Get-Module -Name 'Blade') )
{
    Remove-Module 'Blade'
}

$headingMap = @{ }

& (Join-Path -Path $silkRoot -ChildPath 'Import-Silk.ps1' -Resolve)

try
{
    Convert-ModuleHelpToHtml -ModuleName 'Silk' -HeadingMap $headingMap -Script 'Import-Silk.ps1' |
        ForEach-Object { Out-HtmlPage -Title ('PowerShell - {0} - Silk' -f $_.Name) -VirtualPath ('{0}.html' -f $_.Name) -Content $_.Html }
}
finally
{
}

$tagsPath = Join-Path -Path $PSScriptRoot -ChildPath 'tags.json'
New-ModuleHelpIndex -TagsJsonPath $tagsPath -ModuleName 'Silk' -Script 'Import-Silk.ps1' |
     Out-HtmlPage -Title 'PowerShell - Silk Module Documentation' -VirtualPath '/documentation.html'

$moduleTitle = 'Silk: PowerShell module for PowerShell module authors and projects'
Get-Item -Path (Join-Path -Path $PSScriptRoot -ChildPath 'Silk\en-US\about_Silk.help.txt') |
    Convert-AboutTopicToHtml -ModuleName 'Silk' -Script 'Import-Silk.ps1' |
    ForEach-Object {
        $_ -replace '<h1>about_Silk</h1>','<h1>Silk</h1>'
    } |
    Out-HtmlPage -Title $moduleTitle -VirtualPath '/index.html'

$releaseNotesPath = Join-Path -Path $PSScriptRoot -ChildPath 'RELEASE_NOTES.md' 
Get-Content -Path $releaseNotesPath -Raw | 
    Edit-HelpText -ModuleName 'Silk' |
    Convert-MarkdownToHtml | 
    Out-HtmlPage -Title ('Release Notes - {0}' -f $moduleTitle) -VirtualPath '/releasenotes.html'

$silkCssPath = Join-Path -Path $silkRoot -ChildPath 'Resources\silk.css' -Resolve
$webroot = Join-Path -Path $PSScriptRoot -ChildPath 'get-silk.org'
Copy-Item -Path $silkCssPath -Destination $webroot -Verbose
Silk\Examples\Publish-Module.ps1
<#
.SYNOPSIS
Packages and publishes Silk packages.

.DESCRIPTION
The `Publish-Silk.ps1` script packages and publishes a version of the Silk module. It uses the version defined in the Silk.psd1 file. Before publishing, it adds the current date to the version in the release notes, updates the module's website, then tags the latest revision with the version number. It then publishes the module to NuGet, Chocolatey, and the PowerShell Gallery. If the version of Silk being published already exists in a location, it is not re-published. If the PowerShellGet module isn't installed, the module is not publishes to the PowerShell Gallery.

.EXAMPLE
Publish-Silk.ps1

Yup. That's it.
#>

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#     http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

[CmdletBinding(SupportsShouldProcess=$true)]
param(
)

#Requires -Version 4
Set-StrictMode -Version Latest

& (Join-Path -Path $PSScriptRoot -ChildPath 'Silk\Import-Silk.ps1' -Resolve)

$silkRoot = Join-Path -Path $PSScriptRoot -ChildPath 'Output\Silk'
$releaseNotesPath = Join-Path -Path $silkRoot -ChildPath 'RELEASE_NOTES.md' -Resolve

$manifestPath = Join-Path -Path $silkRoot -ChildPath 'Silk.psd1'
$manifest = Test-ModuleManifest -Path $manifestPath
if( -not $manifest )
{
    return
}

$nupkgPath = Join-Path -Path $PSScriptRoot `
                       -ChildPath ('Output\nuget.org\Silk.PowerShell.{0}.nupkg' -f $manifest.Version)
Publish-NuGetPackage -NupkgPath $nupkgPath

$nupkgPath = Join-Path -Path $PSScriptRoot `
                       -ChildPath ('Output\chocolatey.org\Silk.{0}.nupkg' -f $manifest.Version)
Publish-ChocolateyPackage -NupkgPath $nupkgPath

$tags = @( 'powershell', 'module', 'help', 'tools' )

Publish-PowerShellGalleryModule -ManifestPath $manifestPath `
                                -ModulePath $silkRoot `
                                -ReleaseNotesPath $releaseNotesPath `
                                -LicenseUri 'http://www.apache.org/licenses/LICENSE-2.0' `
                                -ProjectUri 'http://get-silk.org' `
                                -Tags $tags
Silk\Functions\Convert-AboutTopicToHtml.ps1
4
function Convert-AboutTopicToHtml
{
    <#
    .SYNOPSIS
    Converts an about topic into HTML.

    .DESCRIPTION
    The `Convert-AboutTopicToHtml` converts a PowerShell about topic into HTML. The about topic is expected to be [formatted like PowerShell's internal topics](https://msdn.microsoft.com/en-us/library/Dd878343.aspx):

        TOPIC
            about_<subject or module name>
        
        SHORT DESCRIPTION
            A short, one-line description of the topic contents.
        
        LONG DESCRIPTION
            A detailed, full description of the subject or purpose of the module.
        
        EXAMPLES
            Examples of how to use the module or how the subject feature works in practice.
        
        KEYWORDS
            Terms or titles on which you might expect your users to search for the information in this topic.
        
        SEE ALSO
            Text-only references for further reading. Hyperlinks cannot work in the Windows PowerShell console. 
    
    `Convert-AboutTopicToHtml` does the following:
    
     * Removes the `TOPIC` AND `SHORT DESCRIPTION` headers
     * Wraps the topic name in an <h1> tag
     * Renames the `LONG DESCRIPTION` heading to '<h2>Description</h2>
     * Wraps all other headers in <h2> elements.
     * Converts the bodies of each section to HTML 

    Lines that don't begin with spaces are assumed to be headers.

    Lines that begin with spaces are assumed to be content written in Markdown.

    The `SEE ALSO` section is parsed, one line at a time for links, command names, and other help topics, e.g.

        SEE ALSO
            https://msdn.microsoft.com/en-us/library/Dd878343.aspx
            about_Silk
            Convert-AboutTopicToHtml

    Would convert into a three item list, the first a link to the web, the second a link to the `about_Silk.html` topic, and the third to the `Convert-AboutTopicToHtml.html` page.
    #>
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        # The name of the help topic, include the `about_` prefix, or a `FileInfo` object representing the help topic, or the help topic as a giant string.
        $InputObject,
        
        [string]
        # The name of the topic you're converting. Only used if `InputObject` is the text of the about topic.
        $TopicName,

        [Parameter(Mandatory=$true)]
        [string]
        # The name of the module being documented.
        $ModuleName,

        [string]
        # The heading used for the topic's name. Default is `TOPIC`.
        $TopicHeading = 'TOPIC',

        [string]
        # The heading used for the topic's short description. Default is `SHORT DESCRIPTION`.
        $ShortDescriptionHeading = 'SHORT DESCRIPTION',

        [string]
        # The heading used for the topic's long description. Default is `LONG DESCRIPTION`.
        $LongDescriptionHeading = 'LONG DESCRIPTION',

        [string]
        # The heading used for the topic's `See Also` section. Default is `SEE ALSO`.
        $SeeAlsoHeading = 'SEE ALSO',

        [hashtable]
        # A hashtable of headings to use. They key should be the section name. The value should be the heading name.
        $HeadingMap = @{},

        [string[]]
        # The names of any scripts in the module.
        $Script
    )

    begin
    {
        Set-StrictMode -Version 'Latest'
    }

    process
    {

        function Complete-Section
        {
            param(
                [string]
                $Heading,

                [Parameter(Mandatory=$true)]
                [AllowEmptyString()]
                [string]
                $Body
            )

            $Body = $Body.Trim()
            switch( $Heading )
            {
                $TopicHeading
                {
                    # We just don't want to do any Markdown conversion.
                }

                $SeeAlsoHeading
                {
                    $lines = $Body -split ([Environment]::NewLine) | 
                                Convert-RelatedLinkToHtml -ModuleName $ModuleName -Script $Script | 
                                ForEach-Object { '<li>{0}</li>' -f $_ }
                    $Body = @'
    <ul>
        {0}
    </ul>
'@ -f ($lines -join [Environment]::NewLine)
                }
                default
                {
                    $Body = $Body | Edit-HelpText -ModuleName $ModuleName | Convert-MarkdownToHtml 
                }
            }

            $topic | Add-Member -Name $Heading -MemberType NoteProperty -Value $Body
        }

        if( $InputObject -is [IO.FileInfo] )
        {
            [string[]]$lines = $InputObject | Get-Content
            $TopicName = $InputObject.BaseName -replace '\.help$' -f ''
        }
        elseif( $InputObject -is [string] -and $InputObject -match '^about_' )
        {
            [string[]]$lines = Get-Help -Name $InputObject
            if( -not $lines )
            {
                Write-Error ('About topic ''{0}'' not found.' -f $InputObject)
                return
            }
            $TopicName = $InputObject
        }
        else
        {
            $lines = $InputObject -split ([Environment]::NewLine)
        }

        $topic = [pscustomobject]@{ }
        $currentHeader = $null
        $currentContent = $null
        $sectionOrder = New-Object 'Collections.Generic.List[string]'
        $lastLineIdx = $lines.Count - 1
        for( $idx = 0; $idx -lt $lines.Count; ++$idx )
        {
            $line = $lines[$idx]

            if( -not $line -or $line -match '^\s+' )
            {
                if( $line.StartsWith('    ') )
                {
                    $line = $line -replace '^    ',''
                }
                elseif( $line.StartsWith('  ') )
                {
                    $line = $line -replace '^  ',''
                }

                [void]$currentContent.AppendLine( $line )
                if( $idx -eq $lastLineIdx )
                {
                    Complete-Section -Heading $currentHeader -Body $currentContent.ToString()
                }

                continue

            }
            else
            {
                # Header
                if( $currentHeader )
                {
                    Complete-Section -Heading $currentHeader -Body $currentContent.ToString()
                }

                $currentContent = New-Object 'Text.StringBuilder'
                $currentHeader =  [Globalization.CultureInfo]::CurrentCulture.TextInfo.ToTitleCase( $line.ToLowerInvariant() )
                $sectionOrder.Add( $currentHeader )
            }
        }

        if( -not ($topic | Get-Member -Name $TopicHeading) )
        {
            Write-Warning ('Topic ''{0}'' doesn''t have a ''{1}'' heading. Defaulting to {0}. Use the `TopicHeading` parameter to set the topic''s topic heading.' -f $TopicName,$TopicHeading)
            Complete-Section -Heading 'TOPIC' -Body $TopicName
        }

        if( -not ($topic | Get-Member -Name $ShortDescriptionHeading) )
        {
            Write-Warning ('Topic ''{0}'' doesn''t have a ''{1}'' heading. Use the `ShortDescription` parameter to set the topic''s SHORT DESCRIPTION heading.' -f $TopicName,$ShortDescriptionHeading)
            Complete-Section -Heading 'SHORT DESCRIPTION' -Body ''
        }

        if( -not $HeadingMap.ContainsKey($LongDescriptionHeading) )
        {
            $HeadingMap[$LongDescriptionHeading] = 'Description'
        }

        $content = New-Object 'Text.StringBuilder'
        foreach( $section in $sectionOrder )
        {
            if( $section -eq $TopicHeading -or $section -eq $ShortDescriptionHeading )
            {
                continue
            }

            $heading = $section
            if( $HeadingMap.ContainsKey($section) )
            {
                $heading = $HeadingMap[$section]
            }
            [void]$content.AppendLine( ('<h2>{0}</h2>' -f $heading) )
            [void]$content.AppendLine( $topic.$Section )
        }

        @'
    <h1>{0}</h1>

    {1}

    {2}

'@ -f $topic.$TopicHeading,$topic.$ShortDescriptionHeading,$content

    }

    end
    {
    }
}
Silk\Functions\Convert-HelpToHtml.ps1

function Convert-HelpToHtml 
{
    <#
    .SYNOPSIS
    Converts a command's help topic to HTML.

    .DESCRIPTION
    The `Convert-HelpToHtml` function convert's a command's help topic to HTML. This HTML can then be used in a complete HTML page. It will output the following parts of a help topic, in the following order:

     * Name (in an `h1` element). You can override this name with the `DisplayName` parameter.
     * Synopsis
     * Syntax (which you can override with the `Syntax` parameter)
     * Description
     * Related Commands (i.e. `.LINK`)
     * Parameters
     * Parameters
     * Input Types (i.e. `.INPUTS`)
     * Return Types (i.e. `.OUTPUTS`)
     * Notes
     * Examples

    `Convert-HelpToHtml` converts all help text to HTML using [MarkdownSharp](https://code.google.com/p/markdownsharp/), a C# [Markdown](https://daringfireball.net/projects/markdown/) implementation. Markdown "allows you to write using an easy-to-read, easy-to-write plain text format, [that is converted] to structurally valid XHTML (or HTML)". This makes your help web-friendly.

    If you want to convert an entire module's help to HTML, including any scripts and DSC resources, use the `Convert-ModuleHelpToHtml` function. When converting help for a module, pass the module's name with the `ModuleName` parameter, and any of the module's commands or about topics found enclosed in backticks (Markdown's span of code indicators) will be converted to an anchor element whose `href` attribute is the command's name with a .html extension. For example, this help text:

    > Silk's `Convert-HelpToHtml` function converts help text written with Markdown into HTML.

    would get converted to

    > Silk's <a href="Convert-HelpToHtml.html">Convert-HelpToHtml</a> function converts help text written with Markdown into HTML.

    `Convert-HelpToHtml` assumes you'll take its output, wrap it in a full HTML page, and save all of these generated pages into the same directory. For example,

        $html = Convert-HelpToHtml -Name 'Convert-HelpToHtml' -ModuleName 'Silk'
        @"
        <!DOCTYPE html>
        <html>
        <head>
            <title>Convert-HelpToHTml</title>
            <link href="silk.css" type="text/css" rel="stylesheet" />
        </head>
        <body>
        $html
        </body>
        </html>
        "@ | Set-Content -Path (Join-Path -Path $webRoot -ChildPath 'Convert-HelpToHtml.html')

    `Convert-HelpToHtml` outputs HTML 5 (or tries to). For the best cross-browser compatability, make sure you define a doctype on each page. This is especially important for Internet Explorer. If you don't use a doctype, IE will display your pages in quirks mode, which won't display things correctly.

    Silk ships with a default cascading stylesheet (CSS) in the `Resources` directory. Copy this file into your webroot and link to it in each of your pages (via the `link` tag). To customize the appearance of your pages, we recommend you create your own stylesheet and link to it in each of your pages. Make all your style changes in yoru stylesheet. Future versions of Silk will contain fixes/enhancements to the default stylesheet. Using your own will make upgrading easier.

    .LINK
    https://daringfireball.net/projects/markdown/

    .LINK
    Convert-ModuleHelpToHtml

    .EXAMPLE
    Convert-HelpToHtml -Name 'Get-Module'

    Demonstrates how to use `Convert-HelpToHtml` to generate help for a command. You can pass multiple command names to the `Name` parameter.

    .EXAMPLE
    Get-Command -Module 'Silk' | Convert-HelpToHtml -ModuleName 'Silk'

    Demonstrates how you can pipe commands to `Convert-HelpToHtml` to generate help for them.

    .EXAMPLE
    Convert-HelpToHtml -Name 'Set-TargetResource' -DisplayName 'My_Dsc_Resource' -Syntax (Get-DscResource -Name 'My_Dsc_Resource' -Syntax)

    Demonstrates how you can document DSC resources. In this case, the resource's `Set-TargetResource` function contains the help to convert. Because the syntax for using a DSC resource is different than a PowerShell function/cmdlet, we pass the resource's syntax with the `Syntax` parameter.
    #>
	param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [string[]]
        # The name of the command(s) to document.
        $Name,

        [string]
        # The display name. When supplied, it is used as the command's name instead of `Name`. Useful if the command name being documented is different than its public name, e.g. DSC resources.
        $DisplayName,

        [string[]]
        # The syntax of the command. Useful when showing syntax for DSC resources.
        #
        # You can get the syntax for a DSC resource with the `Get-DscResource` cmdlet:
        # 
        #     Get-DscResource -Name 'My_Dsc_Resource' -Syntax
        $Syntax,

        [string]
        # The name of the module whose help is getting converted. Supplying this value will cause any of the module's cmdlets/functions surrounded by backticks (e.g. `Convert-HelpToHtml`) in help text to get replaced with an HTML link to that command's help topic.
        $ModuleName,

        [string[]]
        # The names of any scripts in the module. Supplying these values will cause any script names surround in backticks (e.g. `script.ps1`) in help text to get replaced with an HTML link to that script's help topic.
        $Script
    )

    begin
    {
        Set-StrictMode -Version 'Latest'
    }

    process
    {

        foreach( $commandName in $Name )
        {
            $html = New-Object 'Text.StringBuilder'

            $fullCommandName = $commandName
            if( (Get-Help -Name $commandName | Measure-Object).Count -gt 1 )
            {
                $fullCommandName = '{0}\{1}' -f $ModuleName,$commandName
            }
            Write-Verbose -Message $fullCommandName
            $help = Get-Help -Name $fullCommandName -Full

            if( -not $DisplayName )
            {
                $DisplayName = $commandName
                if( [IO.Path]::IsPathRooted($DisplayName) )
                {
                    $DisplayName = Split-Path -Leaf -Path $DisplayName
                }
            }
        
            [void]$html.AppendFormat( '<h1>{0}</h1>{1}', $DisplayName, [Environment]::NewLine )

            $synopsis = $help.Synopsis | Convert-MarkdownToHtml
            if( $synopsis )
            {
                [void]$html.AppendFormat( '<div class="Synopsis">{0}{1}{0}</div>{0}', [Environment]::NewLine, $synopsis )
            }

            if( -not $Syntax )
            {
                $help.Syntax |
                    ForEach-Object { $_.syntaxItem } |
                    Where-Object { [IO.Path]::IsPathRooted($_.name) } |
                    ForEach-Object { $_.Name = Split-Path -Leaf -Path $_.name }

                $Syntax = $help.Syntax | Out-HtmlString | Format-ForHtml | ForEach-Object { $_ -split "`n" }
            }

            if( $Syntax )
            {
                [void]$html.AppendLine( @"

<h2>Syntax</h2>
<pre class="Syntax"><code>{0}</code></pre>
"@ -f ($Syntax -join "</code></pre>$([Environment]::NewLine)<pre class=""Syntax""><code>") )
            }

            $description = $null
            if( $help | Get-Member -Name 'Description' )
            {    
                $description = $help.Description | Out-HtmlString | Convert-MarkdownToHtml
            }
            if( $description )
            {
                [void]$html.AppendLine( @"

<h2>Description</h2>
<div class="Description">
$description
</div>
"@ )
            }
    
            [string[]]$relatedCommands = $help | Convert-RelatedLinkToHtml -ModuleName $ModuleName -Script $Script
    
            if( $relatedCommands )
            {
                $relatedCommands = $relatedCommands | ForEach-Object { "<li>{0}</li>" -f $_ }
                [void]$html.AppendLine( @"

<h2>Related Commands</h2>

<ul class="RelatedCommands">
{0}
</ul>
"@ -f ($relatedCommands -join ([Environment]::NewLine)) )
            }
    
            $commonParameterNames = @{
                                        'Verbose' = $true;
                                        'Debug' = $true;
                                        'WarningAction' = $true;
                                        'WarningVariable' = $true;
                                        'ErrorAction' = $true;
                                        'ErrorVariable' = $true;
                                        'OutVariable' = $true;
                                        'OutBuffer' = $true;
                                        'WhatIf' = $true;
                                        'Confirm' = $true;
                                    }
            $hasCommonParameters = $false
            $parameters = $help | 
                            Select-Object -ExpandProperty 'Parameters' |
                            Where-Object { $_ | Get-Member -Name 'parameter' } |
                            Select-Object -ExpandProperty 'parameter' |
                            Where-Object { $_ } | 
                            ForEach-Object {
                                if( $commonParameterNames.ContainsKey( $_.name ) )
                                {
                                    $hasCommonParameters = $true
                                }
            
                                $defaultValue = '&nbsp;'
                                if( $_ | Get-Member -Name 'DefaultValue' )
                                {
                                    $defaultValue = $_.DefaultValue
                                }
                                $typeLink = Get-TypeDocumentationLink -CommandName $commandName -TypeName $_.type.name
                                $paramDescription = $_ | 
                                                        Where-Object { $_ | Get-Member -name 'Description' } |
                                                        Select-Object -ExpandProperty 'Description' |
                                                        Out-HtmlString | 
                                                        Convert-MarkdownToHtml
                            @"
<tr valign='top'>
	<td>{0}</td>
	<td>{1}</td>
	<td class="ParamDescription">{2}</td>
	<td>{3}</td>
	<td>{4}</td>
    <td>{5}</td>
</tr>
"@ -f $_.Name,$typeLink,$paramDescription,$_.Required,$_.PipelineInput,$defaultValue
                    }
        
            if( $parameters )
            {
                $commonParameters = ''
                if( $hasCommonParameters )
                {
                    $commonParameters = @"
<tr valign="top">
    <td><a href="http://technet.microsoft.com/en-us/library/dd315352.aspx">CommonParameters</a></td>
    <td></td>
    <td>This cmdlet supports common parameters.  For more information type <br> <code>Get-Help about_CommonParameters</code>.</td>
    <td></td>
    <td></td>
    <td></td>
</tr>
"@
                }
                [void]$html.AppendLine( (@"

<h2> Parameters </h2>
<table id="Parameters">
<tr>
	<th>Name</th>
    <th>Type</th>
	<th>Description</th>
	<th>Required?</th>
	<th>Pipeline Input</th>
	<th>Default Value</th>
</tr>
{0}
{1}
</table>
"@ -f ($parameters -join [Environment]::NewLine),$commonParameters))
            }

            $inputTypes = @()
            if( ($help | Get-Member -Name 'inputTypes') -and ($help.inputTypes | Get-Member 'inputType') )
            {
                $inputTypes = $help.inputTypes.inputType |
                                    Where-Object {  ($_ | Get-Member -Name 'type') -and $_.type -and $_.type.name -match '^([^\s]+)\s*(.*)?$' } |
                                    ForEach-Object { 
                                        $typeLink = Get-TypeDocumentationLink -CommandName $commandName -TypeName $Matches[1].Trim('.')
                                        '{0}. {1}' -f $typeLink,$matches[2]
                                    } |
                                    Convert-MarkdownToHtml
            }

            if( $inputTypes )
            {
                [void]$html.AppendLine( @"

<h2>Input Types</h2>
<div class="InputTypes">
{0}
</div>
"@ -f ($inputTypes -join [Environment]::NewLine))
            }
    
            $returnValues =@()
            if( ($help | Get-Member -Name 'returnValues') -and ($help.returnValues | Get-Member -Name 'returnValue') )
            {
                $returnValues = $help.returnValues.returnValue |
                                    Where-Object {  ($_ | Get-Member -Name 'type') -and $_.type -and $_.type.name -match '^([^\s]+)\s*(.*)?$' } |
                                    ForEach-Object { 
                                        $typeLink = Get-TypeDocumentationLink -CommandName $commandName -TypeName $Matches[1].Trim('.')
                                        '{0}. {1}' -f $typeLink,$matches[2]
                                    } |
                                    Convert-MarkdownToHtml
            }

            if( $returnValues )
            {
                [void]$html.AppendLine( @"

<h2>Return Values</h2>
<div class="ReturnValues">
{0}
</div>
"@ -f ($returnValues -join [Environment]::NewLine))
            }
    
            $notes = ''
            if( $help | Get-Member -Name 'AlertSet' )
            {
                $notes = $help.AlertSet | Out-HtmlString | ForEach-Object { $_ -replace "\r?\n    ",[Environment]::NewLine } | Convert-MarkdownToHtml
            }

            if( $notes )
            {
                [void]$html.AppendLine( @"

<h2>Notes</h2>
<div class="Notes">
{0}
</div>
"@ -f $notes)
            }
    
            $examples = @()
            if( $help | Get-Member -Name 'Examples' )
            {
                $examples = $help.Examples |
                    Where-Object { $_ } |
                    Where-Object { $_ | Get-Member -Name 'example' } |
                    Select-Object -ExpandProperty 'example' |
                    ForEach-Object {
                        $title = $_.title.Trim(('-',' '))
                        $code = ''
                        if( $_.code )
                        {
                            $code = $_.code | Out-HtmlString
                            $code = '<pre><code>{0}</code></pre>' -f $code
                        }
                        $remarks = $_.remarks | Out-HtmlString | Convert-MarkdownToHtml
                        @"

<h2>{0}</h2>
{1}
{2}
"@ -f $title,$code,($remarks -join [Environment]::NewLine)
                }
            }

            if( $examples )
            {
                [void]$html.AppendLine( ($examples -join ([Environment]::NewLine * 2)) )
            }

            $html.ToString()
        }
    }

    end
    {
    }
}

Silk\Functions\Convert-MarkdownToHtml.ps1

filter Convert-MarkdownToHtml
{
    if( $_ )
    {
        $markdown.Transform( $_ ).Trim()
    }
}
Silk\Functions\Convert-ModuleHelpToHtml.ps1

function Convert-ModuleHelpToHtml
{
    <#
    .SYNOPSIS
    Converts a module's help into HTML.

    .DESCRIPTION
    The `Convert-ModuleHelpToHtml` function converts a module's help into HTML. It returns an object for each command and about help topic in the module. The object 
    #>
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]
        # The name of the module whose help to convert.
        $ModuleName,

        [string[]]
        # Any scripts included in the module whose help should get generated. Scripts are assumed to be in the root of the module.
        $Script,

        [hashtable]
        # A hashtable of headings to use. They key should be the section name. The value should be the heading name. Only used when converting about help topics to HTML.
        $HeadingMap,

        [Switch]
        # Don't generate help for individual commands.
        $SkipCommandHelp
    )

    Set-StrictMode -Version 'Latest'

    $commands = Get-Command -Module $ModuleName -CommandType Cmdlet,Function,Filter 

    $moduleBase = Get-Module -Name $ModuleName |
                        Select-Object -ExpandProperty 'ModuleBase'


    $aboutTopics = @()
    if( (Test-Path -Path (Join-Path -Path $moduleBase -ChildPath 'en-US') -PathType Container) )
    {
        $aboutTopics = Get-ChildItem -Path $moduleBase -Filter 'en-US\about_*.help.txt'
    }

    $dscResources = Join-Path -Path $moduleBase -ChildPath 'DscResources' |
                        Where-Object { Test-Path -Path $_ -PathType Container } |
                        Get-ChildItem -Directory 

    $scripts = @()
    if( $Script ) 
    {
        $scripts = $Script | 
                        ForEach-Object { Join-Path -Path $moduleBase -ChildPath $_ } |
                        Get-Item
    }

    [int]$numCommands = $commands | Measure-Object | Select-Object -ExpandProperty 'Count'
    [int]$numScripts = $scripts | Measure-Object | Select-Object -ExpandProperty 'Count'
    [int]$numAboutTopics = $aboutTopics | Measure-Object | Select-Object -ExpandProperty 'Count'
    [int]$numDscResources = $dscResources | Measure-Object | Select-Object -ExpandProperty 'Count'

    [int]$numPages = $numAboutTopics + $numDscResources + $numScripts
    if( -not $SkipCommandHelp )
    {
        $numPages += $numCommands
    }

    $activity = 'Generating {0} Module HTML' -f $ModuleName
    $count = 0
    foreach( $command in $commands )
    {
        if( -not $SkipCommandHelp )
        {
            Write-Progress -Activity $activity -PercentComplete ($count++ / $numPages * 100) -CurrentOperation $command.Name -Status 'Commands'
            $html = Convert-HelpToHtml -Name $command.Name -Script $Script -ModuleName $ModuleName
            [pscustomobject]@{
                                Name = $command.Name;
                                Type = 'Command';
                                Html = $html;
                             }
        }
    }

    foreach( $scriptItem in $scripts )
    {
        Write-Progress -Activity $activity -PercentComplete ($count++ / $numPages * 100) -CurrentOperation $command.Name -Status 'Scripts'
        $html = Convert-HelpToHtml -Name $scriptItem.FullName -ModuleName $ModuleName -Script $Script
        [pscustomobject]@{
                            Name = $scriptItem.Name;
                            Type = 'Script'
                            Html = $html;
                         }
    }

    foreach( $aboutTopic in $aboutTopics )
    {
        $topicName = $aboutTopic.BaseName -replace '\.help',''
        Write-Progress -Activity $activity -PercentComplete ($count++ / $numPages * 100) -CurrentOperation $topicName -Status 'About Topics'
        $html = $aboutTopic | Convert-AboutTopicToHtml -ModuleName $ModuleName -Script $Script
        [pscustomobject]@{
                            Name = $topicName;
                            Type = 'AboutTopic';
                            Html = $html
                         }
    }

    foreach( $dscResource in $dscResources )
    {
        $dscResourceName = $dscResource.BaseName
        Write-Progress -Activity $activity -PercentComplete ($count++ / $numPages * 100) -CurrentOperation $dscResourceName -Status 'DSC Resources'
        Import-Module -Name $dscResource.FullName
        $html = Convert-HelpToHtml -Name 'Set-TargetResource' -DisplayName $dscResourceName -Syntax (Get-DscResource -Name $dscResourceName -Syntax) -ModuleName $ModuleName -Script $Script
        [pscustomobject]@{
                            Name = $dscResourceName;
                            Type = 'DscResource';
                            Html = $html;
                         }
    }
}
Silk\Functions\Convert-RelatedLinkToHtml.ps1

function Convert-RelatedLinkToHtml
{
    <#
    .SYNOPSIS
    Converts a command's related link to HTML.

    .DESCRIPTION
    `Convert-RelatedLinkToHtml` converts a command's related links to HTML. If the related link is not a URL, the command name is converted to a link that poitns to a `CommandName.html` file.


    #>
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        # The help object returned by `Get-Help`.
        $CommandHelp,

        [string]
        # The name of the module the command is in.
        $ModuleName,

        [string[]]
        # The names of any scripts in the module.
        $Script
    )

    begin
    {
        Set-StrictMode -Version 'Latest'

    }

    process
    {
        if( -not $ModuleName -and ($CommandHelp | Get-Member -Name 'ModuleName') -and $CommandHelp.ModuleName )
        {
            $ModuleName = $CommandHelp.ModuleName
        }

        $aboutTopics = @()
        if( $ModuleName )
        {
            $moduleBase = Get-Module -Name $ModuleName |  Select-Object -ExpandProperty 'ModuleBase'
            if( $moduleBase -and (Test-Path -Path (Join-Path -Path $moduleBase -ChildPath 'en-US') -PathType Container) )
            {
                $aboutTopics = $moduleBase | 
                                    Get-ChildItem -Filter 'en-US\about_*' | 
                                    Select-Object -ExpandProperty 'BaseName' | 
                                    ForEach-Object { $_ -replace '\.help$','' }
            }
            else
            {
                $aboutTopics = @()
            }
        }

        Invoke-Command -ScriptBlock {
                if( $CommandHelp | Get-Member -Name 'RelatedLinks' )
                {
                     return $CommandHelp.RelatedLinks |
                                Out-String -Width ([Int32]::MaxValue) |
                                ForEach-Object { $_ -split "`n" } |
                                ForEach-Object { $_.Trim() } |
                                Where-Object { $_ }
                }

                if( $CommandHelp -is [string] )
                {
                    return $CommandHelp
                }
            } |
            ForEach-Object {
                if( $_ -match '^https?\:\/\/' )
                {
                    return '<a href="{0}">{0}</a>' -f $_
                }

                if( $ModuleName -and (Get-Command -Name $_ -Module $ModuleName -ErrorAction Ignore) )
                {
                    $cmdName = $_
                    $alias = Get-Alias -Name $_ -ErrorAction Ignore | Where-Object { $_.ModuleName -eq $ModuleName }
                    if( $alias )
                    {
                        $cmdName = $alias.ReferencedCommand
                    }
                    return '<a href="{0}.html">{1}</a>' -f $cmdName,$_
                }

                $cmd = Get-Command -Name $_ -ErrorAction Ignore
                if( $cmd -and $cmd.HelpUri )
                {
                    return '<a href="{0}.html">{1}</a>' -f $cmd.HelpUri,$_
                }

                if( $aboutTopics -contains $_ -or $Script -contains $_ )
                {
                    return '<a href="{0}.html">{0}</a>' -f $_
                }

                return $_
            }
    }

    end
    {
    }
}
Silk\RELEASE_NOTES.md
# 0.2.0 (21 November 2016)

 * Improved `about_Silk` help topic.
 * Added example scripts `Invoke-Build.ps1`, `Publish-Module.ps1`, and `New-Website.ps1` to show how to use Silk.
Silk\Resources\silk.css
 
Silk\NOTICE
 
Silk\LICENSE
 
Silk\Import-Silk.ps1
<#
.SYNOPSIS
Imports the Silk module.

.DESCRIPTION
Imports the Silk module.  If the Silk module is already loaded, it will remove it and then reloaded.

.EXAMPLE
Import-Silk.ps1

Imports the Silk module, re-loading it if its already loaded.

#>

# Copyright 2012 Aaron Jensen
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#     http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

[CmdletBinding()]
param(
)

Set-StrictMode -Version Latest
$PSScriptRoot = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition

if( (Get-Module Silk) )
{
    Remove-Module Silk -WhatIf:$false
}

Import-Module (Join-Path $PSScriptRoot Silk.psd1 -Resolve) -ErrorAction Stop
Tools\chocolateyUninstall.ps1
<#
.SYNOPSIS
Chocolately install script for Silk.
#>

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#     http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.[CmdletBinding()]

param(
)

#Requires -Version 4
Set-StrictMode -Version 'Latest'
$ErrorActionPreference = 'Stop'

$errorCount = $Global:Error.Count
try
{
    $env:PSModulePath -split ';' |
        Where-Object { $_ } | 
        Join-Path -ChildPath 'Silk' |
        Where-Object { Test-Path -Path $_ -PathType Container } |
        Rename-Item -NewName { 'Silk{0}' -f [IO.Path]::GetRandomFileName() } -PassThru |
        Remove-Item -Recurse -Force
}
finally
{
    for( $idx = $errorCount; $idx -lt $Global:Error.Count; ++$idx )
    {
        $Global:Error[$idx]
        $Global:Error[$idx] | Format-List -Property '*' -Force | Out-String | Write-Verbose
    }
}
Silk\Functions\Set-ReleaseNotesReleaseDate.ps1

function Set-ReleaseNotesReleaseDate
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]
        # The path to the module manifest whose release notes to update.
        $ManifestPath,

        [Parameter(Mandatory=$true)]
        [string]
        # The path to the release notes file.
        $ReleaseNotesPath
    )

    Set-StrictMode -Version 'Latest'

    $manifest = Test-ModuleManifest -Path $ManifestPath
    if( -not $manifest )
    {
        return
    }

    $setHeader = $false
    $releaseNotes = Get-Content -Path $ReleaseNotesPath |
                        ForEach-Object {
                            if( $_ -match '^# {0}\s*$' -f [regex]::Escape($manifest.Version.ToString()) )
                            {
                                $setHeader = $true
                                return "# {0} ({1})" -f $manifest.Version,((Get-Date).ToString("d MMMM yyyy"))
                            }
                            return $_
                        }
    if( $setHeader )
    {
        $releaseNotes | Set-Content -Path $releaseNotesPath
    }
}
Silk\Functions\Set-ModuleVersion.ps1

function Set-ModuleVersion
{
    <#
    .SYNOPSIS
    Updates a module's version.
    #>
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]
        # The path to the module's manifest.
        $ManifestPath,

        [string]
        # The path to the module's manifest.
        $SolutionPath,

        [string]
        # Path to an C# file to update with the assembly version.
        $AssemblyInfoPath,

        [string]
        # Path to a release notes file.
        $ReleaseNotesPath,

        [string]
        # Path to the module's Nuspec file.
        $NuspecPath,

        [Version]
        # The version to build. If not provided, pulled from the module's manifest.
        $Version,

        [string]
        # The pre-release version, e.g. alpha.39, rc.1, etc.
        $PreReleaseVersion,

        [string]
        # Build metadata.
        $BuildMetadata
    )

    Set-StrictMode -Version 'Latest'

    if( -not $Version )
    {
        $Version = Test-ModuleManifest -Path $ManifestPath | Select-Object -ExpandProperty 'Version'
        if( -not $Version )
        {
            return
        }
    }

    if( $Version.Build -lt 0 )
    {
        Write-Error ('Version number must have a build number, i.e. it must have three parts.' -f $Version)
        return
    }

    if( $Version.Revision -ge 0 )
    {
        Write-Error ('Version number must not have a revision number, i.e. it must only have three parts.' -f $Version)
        return
    }

    $manifest = Test-ModuleManifest -Path $ManifestPath
    if( -not $manifest )
    {
        return
    }

    $moduleVersionRegex = 'ModuleVersion\s*=\s*(''|")([^''"])+(''|")' 
    $rawManifest = Get-Content -Raw -Path $manifestPath
    if( $rawManifest -notmatch ('ModuleVersion\s*=\s*(''|"){0}(''|")' -f [regex]::Escape($version.ToString())) )
    {
        $rawManifest = $rawManifest -replace $moduleVersionRegex,('ModuleVersion = ''{0}''' -f $version)
        $rawManifest | Set-Content -Path $manifestPath -NoNewline
    }

    if( $AssemblyInfoPath )
    {
        $assemblyVersionRegex = 'Assembly(File|Informational)?Version\("[^"]*"\)'
        $assemblyVersion = Get-Content -Path $AssemblyInfoPath |
                                ForEach-Object {
                                    if( $_ -match $assemblyVersionRegex )
                                    {
                                        $infoVersion = ''
                                        if( $Matches[1] -eq 'Informational' )
                                        {
                                            if( $PreReleaseVersion )
                                            {
                                                $infoVersion = '-{0}' -f $PreReleaseVersion
                                            }
                                            if( $BuildMetadata )
                                            {
                                                $infoVersion = '{0}+{1}' -f $infoVersion,$BuildMetadata
                                            }
                                        }
                                        return $_ -replace $assemblyVersionRegex,('Assembly$1Version("{0}{1}")' -f $Version,$infoVersion)
                                    }
                                    elseif( $_ -match 'AssemblyCopyright' )
                                    {
                                        return $_ -replace '\("[^"]*"\)',('("{0}")' -f $manifest.Copyright)
                                    }
                                    $_
                                }
        $assemblyVersion | Set-Content -Path $AssemblyInfoPath
    }

    if( $ReleaseNotesPath )
    {
        $newVersionHeader = "# {0}" -f $Version
        $updatedVersion = $false
        $releaseNotes = Get-Content -Path $releaseNotesPath |
                            ForEach-Object {
                                if( -not $updatedVersion -and $_ -match '^#\s+' )
                                {
                                    $updatedVersion = $true
                                    return $newVersionHeader
                                }

                                return $_
                            }
        $releaseNotes | Set-Content -Path $releaseNotesPath
    }

    if( $SolutionPath )
    {
        $msbuildRoot = Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\MSBuild\ToolsVersions\12.0 -Name 'MSBuildToolsPath' | Select-Object -ExpandProperty 'MSBuildToolsPath'
        $msbuildExe = Join-Path -Path $msbuildRoot -ChildPath 'MSBuild.exe' -Resolve
        if( -not $msbuildExe )
        {
            return
        }

        & $msbuildExe /target:"clean;build" $SolutionPath /v:m /nologo
    }

    if( $NuspecPath )
    {
        $nuspec = [xml](Get-Content -Raw -Path $nuspecPath)
        if( $nuspec.package.metadata.version -ne $version.ToString() )
        {
            $nuGetVersion = $version -replace '-([A-Z0-9]+)[^A-Z0-9]*(\d+)$','-$1$2'
            $nuspec.package.metadata.version = $nugetVersion
            $nuspec.Save( $nuspecPath )
        }
    }
}
Silk\Functions\Set-ModuleNuspec.ps1

function Set-ModuleNuspec
{
    param(
        [Parameter(Mandatory=$true)]
        [string]
        # Path to the module's manifest.
        $ManifestPath,

        [Parameter(Mandatory=$true)]
        [string]
        # Path to the module's Nuspec file.
        $NuspecPath,

        [Parameter(Mandatory=$true)]
        [string]
        # Path to the releaes notes file.
        $ReleaseNotesPath,

        [string]
        # The ID of the package. If not provided, the existing ID in the .nuspec file is left in place.
        $PackageID,

        [string]
        # The title of the package. If not provided, the existing title in the .nuspec file is left in place.
        $PackageTitle,

        [string[]]
        # Tags to add to the manifest. Tags are space-delimited, so tags shouldn't have spaces.
        $Tags
    )

    Set-StrictMode -Version 'Latest'

    $NuspecPath = Resolve-Path -Path $NuspecPath
    if( -not $NuspecPath )
    {
        return
    }

    $nuspec = [xml](Get-Content -Path $NuspecPath -Raw)
    if( -not $nuspec )
    {
        return
    }

    $manifest = Test-ModuleManifest -Path $ManifestPath
    if( -not $manifest )
    {
        return
    }

    $releaseNotes = Get-ModuleReleaseNotes -ManifestPath $ManifestPath -ReleaseNotesPath $ReleaseNotesPath
    if( -not $releaseNotes )
    {
        return
    }

    $nuspecMetadata = $nuspec.package.metadata

    if( $PackageID )
    {
        $nuspecMetadata.id = $PackageID
    }

    if( $PackageTitle )
    {
        $nuspecMetadata.title = $PackageTitle
    }

    $nuspecMetadata.description = $manifest.Description
    $nuspecMetadata.version = $manifest.Version.ToString()
    $nuspecMetadata.copyright = $manifest.Copyright
    $nuspecMetadata.releaseNotes = $releaseNotes
    if( $Tags )
    {
        $nuspecMetadata.tags = $Tags -join ' '
    }

    $nuspec.Save( $NuspecPath )
}
Silk\Functions\Set-ModuleManifestMetadata.ps1
Silk\Functions\Publish-PowerShellGalleryPackage.ps1
#>
# Copyright 2012 Aaron Jensen
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#     http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

function Publish-PowerShellGalleryModule
{
    <#
    .SYNOPSIS
    Publishes a module to the PowerShell gallery.

    .DESCRIPTION
    The `Publish-PowerShellGalleryModule` functin publishes a module to the PowerShell Gallery. If the given version of the module already exists in the Gallery, a warning is written and no other work is done.

    If you don't supply a PowerShell Gallery API key via the `ApiKey` parameter, you'll be prompted for it.

    Returns a `PSGetItemInfo` object if the module gets published (the object returned by the `Find-Module` cmdlet). If the version of the module already exists in the Gallery, you'll get a warning that the module has already been published.

    This function requires the `PowerShellGet` module. If it isn't available, you'll get an error.

    .OUTPUTS
    PSGetItemInfo

    .EXAMPLE
    Publish-PowerShellGalleryModule -Name 'Carbon' -Version '2.0.0' -LicenseUri ''http://www.apache.org/licenses/LICENSE-2.0'
    #>
    [CmdletBinding(SupportsShouldProcess=$true)]
    param(
        [Parameter(Mandatory=$true)]
        [string]
        # Path to the module's manifest.
        $ManifestPath,

        [Parameter(Mandatory=$true)]
        [string]
        # The path to the module.
        $ModulePath,

        [Parameter(Mandatory=$true)]
        [string]
        # The release notes.
        $ReleaseNotesPath,

        [string]
        # The name of the module being published. Defaults to the name in the module manifest.
        $Name,

        [string]
        # The API key for the PowerShell Gallery.
        $ApiKey,

        [Parameter(Mandatory=$true)]
        [string]
        # The URL to the module's license.
        $LicenseUri,

        [string[]]
        # Any tags for the module.
        $Tags,

        [string]
        # The URL to the project's home page.
        $ProjectUri
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState

    $manifest = Test-ModuleManifest -Path $ManifestPath
    if( -not $manifest )
    {
        return
    }

    if( -not $Name )
    {
        $Name = $manifest.Name
    }

    if( Get-Module -ListAvailable -Name 'PowerShellGet' )
    {
        if( -not (Find-Module -Name $Name -RequiredVersion $manifest.Version -Repository 'PSGallery' -ErrorAction Ignore) )
        {
            $releaseNotes = Get-ModuleReleaseNotes -ManifestPath $ManifestPath -ReleaseNotesPath $ReleaseNotesPath
            Write-Verbose -Message ('Publishing to PowerShell Gallery.')
            
            if( $PSCmdlet.ShouldProcess('publish module to PowerShell Gallery','','') )
            {
                if( -not $ApiKey )
                {
                    $ApiKey = Read-Host -Prompt ('Please enter PowerShell Gallery API key')
                }

                Publish-Module -Path $ModulePath `
                               -Repository 'PSGallery' `
                               -NuGetApiKey $ApiKey `
                               -LicenseUri $LicenseUri `
                               -ReleaseNotes $releaseNotes `
                               -Tags $Tags `
                               -ProjectUri $ProjectUri

                Find-Module -Name $Name -RequiredVersion $manifest.Version -Repository 'PSGallery'
            }
        }
        else
        {
            Write-Warning -Message ('{0} {1} already exists in the PowerShell Gallery.' -f $Name,$manifest.Version)
        }
    }
    else
    {
        Write-Error -Message ('Unable to publish to PowerShell Gallery: PowerShellGet module not found.')
    }

}
Silk\Functions\Publish-NuGetPackage.ps1
# Copyright 2012 Aaron Jensen
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#     http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

function Publish-NuGetPackage
{
    <#
    .SYNOPSIS
    Creates and publishes a NuGet package to nuget.org.
    #>
    [CmdletBinding(SupportsShouldProcess=$true)]
    param(
        [Parameter(Mandatory=$true)]
        [string]
        # The path to the module manifest of the module you want to publish.
        $NupkgPath,

        [object]
        # The API key(s) to use. To supply multiple API keys, use a hashtable where each key is a repository server name and the value is the API key for that repository. For example,
        #
        # @{ 'nuget.org' = '395edfa5-652f-4598-868e-c0a73be02c84' }
        #
        # If not specified, you'll be prompted for it. 
        $ApiKey
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState

    $nugetPath = Join-Path -Path $PSScriptRoot -ChildPath '..\bin\NuGet.exe' -Resolve
    if( -not $nugetPath )
    {
        return
    }

    if( -not (Test-Path -Path $NupkgPath -PathType Leaf) )
    {
        Write-Error -Message ('NuGet package ''{0}'' does not exist.' -f $NupkgPath)
        return
    }

    $nupkgName = [IO.Path]::GetFileNameWithoutExtension($NupkgPath)
    if( $nupkgName -notmatch '^(.+)\.(\d+\.\d+\.\d)+$' )
    {
        Write-Error -Message ('NuGet package ''{0}'' does not have the version to publish in its name.' -f $nupkgName)
        return
    }

    $packageName = $Matches[1]
    $version = $Matches[2]
    try
    {
        $packageUrl = 'https://nuget.org/api/v2/package/{0}/{1}' -f $packageName,$version
        try
        {
            $resp = Invoke-WebRequest -Uri $packageUrl -ErrorAction Ignore
            $publish = ($resp.StatusCode -ne 200)
        }
        catch
        {
            $publish = $true
        }

        if( -not $publish )
        {
            Write-Warning ('NuGet package {0} {1} already published to nuget.org.' -f $packageName,$version)
            return
        }

        if( $PSCmdlet.ShouldProcess(('publish package to nuget.org'),'','') )
        {
            if( -not $ApiKey )
            {
                $ApiKey = Read-Host -Prompt ('Please enter your nuget.org API key')
                if( -not $ApiKey )
                {
                    Write-Error -Message ('The nuget.org API key is required. Package not published to nuget.org.')
                    continue
                }
            }

            $verbosity = 'normal'
            if( $VerbosePreference -eq 'Continue' )
            {
                $verbosity = 'detailed'
            }

            & $nugetPath push $nupkgPath -ApiKey $ApiKey -Source 'https://nuget.org/api/v2/package' -Verbosity $verbosity

            $resp = Invoke-WebRequest -Uri $packageUrl
            $resp | Select-Object -Property 'StatusCode','StatusDescription',@{ Name = 'Uri'; Expression = { $packageUrl }}
        }
    }
    finally
    {
        Pop-Location
    }
}
Silk\Functions\Publish-BitbucketDownload.ps1
# Copyright 2012 Aaron Jensen
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#     http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

function Publish-BitbucketDownload
{
    <#
    .SYNOPSIS
    Creates and publishes a ZIP file to Bitbucket so it is available on a project's download page.

    .DESCRIPTION
    The `Publish-BitbucketDownload` function creates a ZIP file and publishes it to a repository so it is availabe on a project's download page. If the file already exists on Bitbucket, nothing is uploaded.

    .LINK
    https://bitbucket.org/Swyter/bitbucket-curl-upload-to-repo-downloads
    #>
    [CmdletBinding()]
    param(
        [pscredential]
        # The Bitbucket credentials to use.
        $Credential,

        [Parameter(Mandatory=$true)]
        [string]
        # The account of the project you're uploading a file to.
        $Username,

        [Parameter(Mandatory=$true)]
        [string]
        # The name of the project you're uploading a file to.
        $ProjectName,

        [Parameter(Mandatory=$true)]
        [string[]]
        # The paths to the files and directories to include in the ZIP file. All files and sub-directories under directory are added.
        $Path,

        [Parameter(Mandatory=$true)]
        [string]
        # The path to the manifest of the module being published.
        $ManifestPath
    )

    Set-StrictMode -Version 'Latest'

    function Assert-Response
    {
        param(
            [Microsoft.PowerShell.Commands.HtmlWebResponseObject]
            $Response,

            [Uri]
            $ExpectedUri
        )

        if( -not $Response )
        {
            Write-Error ('No response.')
            return $false
        }

        if( $Response.BaseResponse.StatusCode -ne [Net.HttpStatusCode]::OK )
        {
            Write-Error ('Response failed.')
            return $false
        }

        if( $Response.BaseResponse.ResponseUri -ne $ExpectedUri )
        {
            Write-Error ('Response didn''t finish on URI {0} ({1}).' -f $ExpectedUri,$Response.BaseResponse.ResponseUri)
            return $false
        }

        $errorElement = $Response.ParsedHtml.getElementById('error')
        if( $errorElement -and ($errorElement | Get-Member 'innerHtml') -and $erroElement.innerHtml )
        {
            Write-Error $errorElement.innerHtml
            return $false
        }

        return $true

    }

    $manifest = Test-ModuleManifest -Path $ManifestPath
    if( -not $manifest )
    {
        return
    }

    $baseProjectUri = 'https://bitbucket.org/{0}/{1}' -f $Username,$ProjectName

    $zipFileName = "{0}-{1}.zip" -f $manifest.Name,$manifest.Version
    $zipDownloadUrl = '{0}/downloads/{1}' -f $baseProjectUri,$zipFileName

    try
    {
        $resp = Invoke-WebRequest -Uri $zipDownloadUrl -ErrorAction Ignore
        $publish = ($resp.StatusCode -ne 200)
    }
    catch
    {
        $publish = $true
    }

    if( -not $publish )
    {
        Write-Warning -Message ('{0} file already published.' -f $zipFileName)
        return
    }

    $zipFilePath = Join-Path -Path $env:TEMP -ChildPath $zipFileName

    $outFile = '{0}+{1}' -f $manifest.Name,[IO.Path]::GetRandomFileName()
    $outFile = Join-Path -Path $env:TEMP -ChildPath $outFile

    try
    {
        if( Test-Path $zipFilePath -PathType Leaf )
        {
            Remove-Item $zipFilePath
        }

        Write-Verbose -Message ('Creating {0} ZIP file.' -f $zipFileName)
        Compress-Item -Path $Path -OutFile $zipFilePath

        $PSDefaultParameterValues.Clear()
        $PSDefaultParameterValues['Invoke-WebRequest:PassThru'] = $true
        $PSDefaultParameterValues['Invoke-WebRequest:OutFile'] = $outFile

        if( -not $Credential )
        {
            $Credential = Get-Credential -Message ('Enter credentials for {0}' -f $baseProjectUri)
        }

        $session = $null
        $loginUri = 'https://bitbucket.org/account/signin/'
        $resp = Invoke-WebRequest -Uri $loginUri -SessionVariable 'session' -Method Get 
        if( -not (Assert-Response -Response $resp -ExpectedUri $loginUri) )
        {
            return
        }

        $PSDefaultParameterValues['Invoke-WebRequest:WebSession'] = $session

        $form = $resp.Forms | 
                    Where-Object { $_.Action -eq '/account/signin/' }
        $formFields = $form.Fields
        $formFields.id_username = $Credential.UserName
        $formFields.id_password = $Credential.GetNetworkCredential().Password

        $loginUri = 'https://bitbucket.org{0}' -f $form.Action
        $body = @{
                        'username' = $Credential.UserName;
                        'password' = $Credential.GetNetworkCredential().Password;
                        'csrfmiddlewaretoken' = $formFields.csrfmiddlewaretoken;
                        'submit' = '';
                        'next' = '';
                        }
        $resp = Invoke-WebRequest -Uri $loginUri -Method $form.Method -Body $body -Headers @{ Referer = $loginUri }
        if( -not (Assert-Response -Response $resp -ExpectedUri 'https://bitbucket.org/') )
        {
            exit 1
        }

        $downloadUri = '{0}/downloads' -f $baseProjectUri
        $resp = Invoke-WebRequest -Uri $downloadUri -Method Get 
        if( -not (Assert-Response -Response $resp -ExpectedUri $downloadUri) )
        {
            exit 1
        }

        $csrfToken = $resp.Forms |
                        Where-Object { $_.Fields.ContainsKey( 'csrfmiddlewaretoken' ) } |
                        ForEach-Object { $_.Fields.csrfmiddlewaretoken }
        Write-Debug $csrfToken

        $boundary = [Guid]::NewGuid().ToString()

        $bodyStart = @"
--$boundary
Content-Disposition: form-data; name="csrfmiddlewaretoken"

$csrfToken
--$boundary
Content-Disposition: form-data; name="token"

--$boundary
Content-Disposition: form-data; name="files"; filename="$(Split-Path -Leaf -Path $zipFilePath)"
Content-Type: application/octet-stream


"@

        $bodyEnd = @"

--$boundary--
"@

        $requestInFile = Join-Path -Path $env:TEMP -ChildPath ([IO.Path]::GetRandomFileName())

        try
        {
            $fileStream = New-Object 'System.IO.FileStream' ($requestInFile, [System.IO.FileMode]'Create', [System.IO.FileAccess]'Write')
    
            try
            {
                $bytes = [Text.Encoding]::UTF8.GetBytes($bodyStart)
                $fileStream.Write( $bytes, 0, $bytes.Length )

                $bytes = [IO.File]::ReadAllBytes($zipFilePath)
                $fileStream.Write( $bytes, 0, $bytes.Length )

                $bytes = [Text.Encoding]::UTF8.GetBytes($bodyEnd)
                $fileStream.Write( $bytes, 0, $bytes.Length )
            }
            finally
            { 
                $fileStream.Close()
            }

            $contentType = 'multipart/form-data; boundary={0}' -f $boundary

            $resp = Invoke-WebRequest -Uri $downloadUri `
                                      -Method Post `
                                      -InFile $requestInFile `
                                      -ContentType $contentType `
                                      -Headers @{ Referer = $downloadUri }
            if( -not (Assert-Response -Response $resp -ExpectedUri $downloadUri) )
            {
                return
            }

        }
        finally
        {
            Remove-Item -Path $requestInFile
        }

        $numTries = 10
        $tryNum = 0
        while( $tryNum++ -lt $numTries )
        {
            try
            {
                $resp = Invoke-WebRequest -Uri $zipDownloadUrl
                $resp | Select-Object -Property 'StatusCode','StatusDescription',@{ Name = 'Uri'; Expression = { $zipDownloadUrl }}
                break
            }
            catch
            {
                Start-Sleep -Seconds 1
            }
        }

    }
    finally
    {
        if( (Test-Path -Path $outFile -PathType Leaf) )
        {
            Remove-Item -Path $outFile
        }

        if( (Test-Path -Path $zipFilePath -PathType Leaf) )
        {
            Remove-Item -Path $zipFilePath
        }
    }
}
Silk\Functions\Out-HtmlString.ps1

filter Out-HtmlString
{
    <#
    .SYNOPSIS
    Writes a string out to the pipeline, trimming whitespace.
    #>
    $_ | 
        Out-String -Width 9999 | 
        ForEach-Object { $_.Trim() } |
        Where-Object { $_ }
}

Silk\Functions\New-NuGetPackage.ps1
# Copyright 2012 Aaron Jensen
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#     http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

function New-NuGetPackage
{
    <#
    .SYNOPSIS
    Creates a NuGet package.
    #>
    [CmdletBinding(SupportsShouldProcess=$true)]
    param(
        [Parameter(Mandatory=$true)]
        [string]
        # The path to the module manifest of the module you want to publish.
        $ManifestPath,

        [Parameter(Mandatory=$true)]
        [string]
        # The path to the nuspec file for the NuGet package to publish.
        $NuspecPath,

        [Parameter(Mandatory=$true)]
        [string]
        # The base directory for the files defined in the `NuspecPath` file.
        $NuspecBasePath,

        [string]
        # The name of the NuGet package, if it is different than the module name.
        $PackageName,

        [Parameter(Mandatory=$true)]
        [string]
        # The directory where the .nupkg file should be saved.
        $OutputDirectory
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState

    $nugetPath = Join-Path -Path $PSScriptRoot -ChildPath '..\bin\NuGet.exe' -Resolve
    if( -not $nugetPath )
    {
        return
    }

    if( -not (Test-Path -Path $OutputDirectory -PathType Container) )
    {
        New-Item -Path $OutputDirectory -ItemType 'directory' -Force
    }

    $manifest = Test-ModuleManifest -Path $ManifestPath
    if( -not $manifest )
    {
        return
    }

    if( -not $PackageName )
    {
        $PackageName = $manifest.Name
    }

    Push-Location -Path $NuSpecBasePath
    try
    {
        $nupkgPath = Join-Path -Path $OutputDirectory -ChildPath ('{0}.{1}.nupkg' -f $PackageName,$manifest.Version)
        if( (Test-Path -Path $nupkgPath -PathType Leaf) )
        {
            Remove-Item -Path $nupkgPath
        }

        $verbosity = 'normal'
        if( $VerbosePreference -eq 'Continue' )
        {
            $verbosity = 'detailed'
        }
        & $nugetPath pack $NuspecPath -BasePath '.' -NoPackageAnalysis -Verbosity $verbosity -OutputDirectory $OutputDirectory
        if( -not (Test-Path -Path $nupkgPath -PathType Leaf) )
        {
            Write-Error ('NuGet package ''{0}'' not found.' -f $nupkgPath)
            return
        }
    }
    finally
    {
        Pop-Location
    }
}
Silk\Functions\New-ModuleHelpIndex.ps1

function New-ModuleHelpIndex
{
    <#
    .SYNOPSIS
    Creates an index page for a module's help.
    #>
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]
        # The name of the module whose index page to create.
        $ModuleName,

        [string[]]
        # The names of any scripts that should be included.
        $Script,

        [string]
        # The path to the tags file. If not provided, no tag tab is generated.
        $TagsJsonPath
    )

    Set-StrictMode -Version 'Latest'

    if( $TagsJsonPath )
    {
        $tagsJson = Get-Content -Path $TagsJsonPath | ConvertFrom-Json

        $tags = @{ }

        foreach( $item in $tagsJson )
        {
            foreach( $tagName in $item.Tags )
            {
                if( -not $tags.ContainsKey( $tagName ) )
                {
                    $tags[$tagName] = New-Object 'Collections.Generic.List[string]'
                }

                $tags[$tagName].Add( $item.Name )
            }
        }

        $tagCloud = $tags.Keys | Sort-Object | ForEach-Object { 

        $commands = $tags[$_] | ForEach-Object { '<li><a href="{0}.html">{0}</a></li>' -f $_ }
        @'
    <h3>{0}</h3>

    <ul>
        {1}
    </ul>
'@ -f $_,($commands -join ([Environment]::NewLine))
        }

    }
    else
    {
        $tagCloud = ''
    }

    $verbs = @{ }

    $commands = Get-Command -Module $ModuleName -CommandType Cmdlet,Function,Filter 
    foreach( $command in $commands )
    {
        if( -not $verbs.ContainsKey( $command.Verb ) )
        {
            $verbs[$command.Verb] = New-Object 'Collections.Generic.List[string]'
        }
        $verbs[$command.Verb].Add( $command.Name )
    }

    $commandList = Invoke-Command {
                                        $commands |  Select-Object -ExpandProperty 'Name'
                                        $moduleBase = Get-Module -Name $ModuleName | Select-Object -ExpandProperty 'ModuleBase'
                                        $dscResourceBase = Join-Path -Path $moduleBase -ChildPath 'DscResources'
                                        if( (Test-Path -Path $dscResourceBase -PathType Container) )
                                        {
                                            Get-ChildItem -Directory -Path $dscResourceBase
                                        }
                                    } |
                        Sort-Object | 
                        ForEach-Object { '<li><a href="{0}.html">{0}</a></li>' -f $_ }
    $commandList = @'
<ul>
    {0}
</ul>
'@ -f ($commandList -join ([Environment]::NewLine))

    $verbList = $verbs.Keys | Sort-Object | ForEach-Object {
        $verb = $_
        $verbCommands = $verbs[$verb] | ForEach-Object { '<li><a href="{0}.html">{0}</a></li>' -f $_ }
        @'
    <h3>{0}</h3>

    <ul>
        {1}
    </ul>
'@ -f $verb,($verbCommands -join ([Environment]::NewLine))
    }

    $scriptContent = ''
    if( $Script )
    {
        $scriptContent = @"
<h2>Scripts</h2>

<ul>
    $($Script | ForEach-Object { '<li><a href="{0}.html">{0}</a></li>' -f $_ })
</ul>
"@
    }

    $topicList = New-Object 'Collections.Generic.List[string]'

    $moduleBase = Get-Module -Name $ModuleName |  Select-Object -ExpandProperty 'ModuleBase'
    $aboutTopics = @()
    if( (Test-Path -Path (Join-Path -Path $moduleBase -ChildPath 'en-US') -PathType Container) )
    {
        $aboutTopics = Get-ChildItem -Path $moduleBase -Filter 'en-US\about_*.help.txt'
    }

    foreach( $aboutTopic in $aboutTopics )
    {
        $topicName = $aboutTopic.BaseName -replace '\.help$',''
        $virtualPath = '{0}.html' -f $topicName
        $topicList.Add( ('<li><a href="{0}">{1}</a></li>' -f $virtualPath,$topicName) )
    }

    function New-CommandsMenuItem
    {
        param(
            $ID,
            $Name
        )

        Set-StrictMode -Version 'Latest'

        if( -not $tagCloud -and $ID -eq 'ByTag' )
        {
            return
        }

        $selectedAttr = ''
        if( ($tagCloud -and $ID -eq 'ByTag') -or ($ID -eq 'ByName' -and -not $tagCloud) )
        {
            $selectedAttr = 'class="selected"'
        }

        '<li id="{0}MenuItem" {1}><a href="#{0}">{2}</a></li>' -f $ID,$selectedAttr,$Name
    }

    function New-CommandContentDiv
    {
        param(
            $ID,
            $Line
        )

        Set-StrictMode -Version 'Latest'

        if( -not $Line )
        {
            return
        }

        $styleAttr = 'display:none;'
        if( ($ID -eq 'Tag' -and $tagCloud) -or ($ID -eq 'Name' -and -not $tagCloud) )
        {
            $styleAttr = ''
        }

        @'
<div id="By{0}Content" style="{2}">
    <a id="By{0}"></a>

    {1}

</div>
'@ -f $ID,($Line -join ([Environment]::NewLine)),$styleAttr
    }

    @"
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script>
jQuery( document ).ready(function() {
    jQuery("#CommandsMenu > li").click( function() {
        var selectedLi = jQuery("#CommandsMenu li.selected")
        selectedLi.removeClass("selected");
        
        var selectedCmdID = selectedLi.attr("id").replace("MenuItem","");
        jQuery("#" + selectedCmdID + 'Content').hide();
        
        var li = jQuery(this);
        li.addClass("selected");
        
        var id = li.attr( 'id' )
        id = id.replace('MenuItem','');
        
        jQuery('#' + id + 'Content').show();
        
        return false;
    });
});
</script>

<h2>About Help Topics</h2>

<ul>
    $($topicList.ToArray() -join ([Environment]::NewLine))
</ul>

$($scriptContent)

<h2>Commands</h1>

<ul id="CommandsMenu">
    $( New-CommandsMenuItem 'ByTag' 'By Tag' )
    $( New-CommandsMenuItem 'ByName' 'By Name' )
    $( New-CommandsMenuItem 'ByVerb' 'By Verb' )
</ul>

<div id="CommandsContent">

    $( New-CommandContentDiv 'Tag' $tagCloud )
    $( New-CommandContentDiv 'Name' $commandList )
    $( New-CommandContentDiv 'Verb' $verbList )

</div>
"@

}
Silk\Functions\New-ChocolateyPackage.ps1
# Copyright 2012 Aaron Jensen
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#     http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

function New-ChocolateyPackage
{
    <#
    .SYNOPSIS
    Creates a NuGet package.
    #>
    [CmdletBinding(SupportsShouldProcess=$true)]
    param(
        [Parameter(Mandatory=$true)]
        [string]
        # The path to the module manifest of the module you want to publish.
        $ManifestPath,

        [Parameter(Mandatory=$true)]
        [string]
        # The path to the nuspec file for the NuGet package to publish.
        $NuspecPath,

        [Parameter(Mandatory=$true)]
        [string]
        # The directory where the .nupkg file should be saved.
        $OutputDirectory
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState

    if( -not (Get-Command -Name 'choco.exe' -ErrorAction Ignore) )
    {
        Write-Error -Message ('choco.exe not found. Please install Chocoatey from chocolatey.org')
        return
    }

    if( -not (Test-Path -Path $OutputDirectory -PathType Container) )
    {
        New-Item -Path $OutputDirectory -ItemType 'directory' -Force
    }

    $manifest = Test-ModuleManifest -Path $ManifestPath
    if( -not $manifest )
    {
        return
    }

    Push-Location -Path $OutputDirectory
    try
    {
        Get-ChildItem -Path '*.nupkg' | Remove-Item

        $verbosity = ''
        if( $VerbosePreference -eq 'Continue' )
        {
            $verbosity = '-v'
        }

        choco.exe pack $NuspecPath --version=$($manifest.Version) $verbosity
        if( -not (Test-Path -Path '*.nupkg' -PathType Leaf) )
        {
            Write-Error -Message 'Chocolatey package not created.'
        }
    }
    finally
    {
        Pop-Location
    }
}
Silk\Functions\Get-Function.ps1

filter Get-Function
{
    <#
    .SYNOPSIS
    Gets all of a module's functions.
    #>
    param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        # The file to parse for functions
        $Path
    )
    
    Write-Verbose "Loading script '$Path'."
    $scriptContent = Get-Content "$Path"
    if( -not $scriptContent )
    {
        return @()
    }

    $errors = [Management.Automation.PSParseError[]] @()
    $tokens = [System.Management.Automation.PsParser]::Tokenize( $scriptContent, [ref] $errors )
    if( $errors -ne $null -and $errors.Count -gt 0 )
    {
        Write-Error "Found $($errors.count) error(s) parsing '$Path'."
        return
    }
    
    Write-Verbose "Found $($tokens.Count) tokens in '$Path'."
    
    for( $idx = 0; $idx -lt $tokens.Count; ++$idx )
    {
        $token = $tokens[$idx]
        if( $token.Type -eq 'Keyword'-and ($token.Content -eq 'Function' -or $token.Content -eq 'Filter') )
        {
            $atFunction = $true
        }
        
        if( $atFunction -and $token.Type -eq 'CommandArgument' -and $token.Content -ne '' )
        {
            Write-Verbose "Found function '$($token.Content).'"
            $token.Content
            $atFunction = $false
        }
    }
}
Silk\Functions\Format-ForHtml.ps1

filter Format-ForHtml 
{
    <#
    .SYNOPSIS
    Encodes text for HTML.
    #>
    if( $_ )
    {
        [Web.HttpUtility]::HtmlEncode($_)
    }
}
Silk\Functions\Edit-HelpText.ps1

function Edit-HelpText
{
    <#
    .SYNOPSIS
    Converts the command names in a block of text to links.

    .DESCRIPTION
    The `Edit-HelpText` function converts all a module's command names or help topic names into Markdown links. The command names or help topic names should be surrounded by backticks, e.g. `Invoke-Function`, `about_Module`.
    #>
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true,VAlueFromPipeline=$true)]
        # The text to convert.
        $InputObject,

        [string]
        # The name of the module whose command names to convert.
        $ModuleName
    )

    begin
    {
        Set-StrictMode -Version 'Latest'

        $commands = Invoke-Command -ScriptBlock {
                                                    Get-Command -Module $ModuleName -CommandType Cmdlet
                                                    Get-Command -Module $ModuleName -CommandType Function
                                                    Get-Command -Module $ModuleName -CommandType Filter
                                                }
        $aliases = Get-Command -Module $ModuleName -CommandType Alias | Get-Alias
        $aboutTopicNames = Get-ChildItem -Path (Get-Module -Name $ModuleName).ModuleBase -Filter 'en-US\about_*' |
                                Select-Object -ExpandProperty 'BaseName' |
                                ForEach-Object { $_ -replace '\.help$','' }
    }

    process
    {
        $regex = $commands | Select-Object -ExpandProperty 'Name' | ForEach-Object { [regex]::Escape( $_ ) }
        $regex = $regex -join '|'
        $regex = '`({0})`' -f $regex
        $replacement = '[$1]($1.html)'

       $InputObject | 
            ForEach-Object { $_ -replace $regex,$replacement } |
            ForEach-Object {
                $text = $_
                foreach( $alias in $aliases )
                {
                    $text = $text -replace ('`({0})`' -f $alias.Name),('[$1]({0}.html)' -f $alias.Definition)
                }

                foreach( $aboutTopicName in $aboutTopicNames )
                {
                    $text = $text -replace ('`({0})`' -f $aboutTopicName),('[{0}]($1.html)' -f ($aboutTopicName -replace '_','\_'))
                }
                return $text
            }
    }

    end
    {
    }
}
Silk\Silk.psd1
# Copyright 2012 Aaron Jensen
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#     http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Module manifest for module 'Silk'
#
# Generated by: Aaron Jensen
#
# Generated on: 7/4/2013
#

@{

# Script module or binary module file associated with this manifest
RootModule = 'Silk.psm1'

# Version number of this module.
ModuleVersion = '0.2.0'

# ID used to uniquely identify this module
GUID = 'b4d24de5-aa00-4a18-bbde-ac8bb641751f'

# Author of this module
Author = 'Aaron Jensen'

# Company or vendor of this module
CompanyName = ''

# Copyright statement for this module
Copyright = '(c) 2013 Aaron Jensen. All rights reserved.'

# Description of the functionality provided by this module
Description = 'PowerShell module for publishing another module''s help system as a website.'

# Minimum version of the Windows PowerShell engine required by this module
PowerShellVersion = ''

# Name of the Windows PowerShell host required by this module
PowerShellHostName = ''

# Minimum version of the Windows PowerShell host required by this module
PowerShellHostVersion = ''

# Minimum version of the .NET Framework required by this module
DotNetFrameworkVersion = ''

# Minimum version of the common language runtime (CLR) required by this module
CLRVersion = ''

# Processor architecture (None, X86, Amd64, IA64) required by this module
ProcessorArchitecture = ''

# Modules that must be imported into the global environment prior to importing this module
RequiredModules = @()

# Assemblies that must be loaded prior to importing this module
RequiredAssemblies = @()

# Script files (.ps1) that are run in the caller's environment prior to importing this module
ScriptsToProcess = @()

# Type files (.ps1xml) to be loaded when importing this module
TypesToProcess = @()

# Format files (.ps1xml) to be loaded when importing this module
FormatsToProcess = @( )

# Modules to import as nested modules of the module specified in ModuleToProcess
NestedModules = @()

# Functions to export from this module
FunctionsToExport = '*'

# Cmdlets to export from this module
CmdletsToExport = '*'

# Variables to export from this module
VariablesToExport = '*'

# Aliases to export from this module
AliasesToExport = '*'

# List of all modules packaged with this module
ModuleList = @()

# List of all files packaged with this module
FileList = @()

# Private data to pass to the module specified in ModuleToProcess
PrivateData = @{

    PSData = @{

        # Tags applied to this module. These help with module discovery in online galleries.
        Tags = @('powershell','module','help','tools')

        # A URL to the license for this module.
        LicenseUri = 'http://www.apache.org/licenses/LICENSE-2.0'

        # A URL to the main website for this project.
        ProjectUri = 'http://get-silk.org'

        # A URL to an icon representing this module.
        # IconUri = ''

        # ReleaseNotes of this module
        ReleaseNotes = @'
* Improved `about_Silk` help topic.
 * Added example scripts `Invoke-Build.ps1`, `Publish-Module.ps1`, and `New-Website.ps1` to show how to use Silk.
'@

    } # End of PSData hashtable

} # End of PrivateData hashtable

}

Silk\Silk.psm1
# Copyright 2013 Aaron Jensen
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#     http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

Add-Type -Path (Join-Path $PSSCriptRoot bin\MarkdownSharp.dll)
$markdown = New-Object MarkdownSharp.Markdown
$markdown.AutoHyperlink = $true

$loadedTypes = @{ }
[AppDomain]::CurrentDomain.GetAssemblies() | 
    ForEach-Object { $_.GetTypes() } | 
    Where-Object { $_.IsPublic } |
    Sort-Object -Property 'Name' |
    ForEach-Object { 
        if( $loadedTypes.ContainsKey( $_.Name ) )
        {
            Write-Verbose ("Found multiple <{0}> types <{1}> <{2}>." -f $_.Name,$_.FullName,$loadedTypes[$_.Name])
        }
        else
        {
            $loadedTypes[$_.Name] = $_.FullName
        }
    }

$filesToSkip = @{
                    'Import-Silk' = $true;
                }

Get-Item (Join-Path -Path $PSScriptRoot -ChildPath 'Functions\*.ps1') | 
    ForEach-Object {
        Write-Debug ("Importing function {0}." -f $_.FullName)
        . $_.FullName
    }
Tools\chocolateyInstall.ps1
<#
.SYNOPSIS
Chocolately install script for Silk.
#>

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#     http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.[CmdletBinding()]

param(
)

#Requires -Version 4
Set-StrictMode -Version 'Latest'
$ErrorActionPreference = 'Stop'

function Get-PowerShellModuleInstallPath
{
    <#
    .SYNOPSIS
    Returns the path to the directory where you can install custom modules.

    .DESCRIPTION
    Custom modules should be installed under the `Program Files` directory. This function looks at the `PSModulePath` environment variable to find the install location under `Program Files`. If that path isn't part of the `PSModulePath` environment variable, returns the module path under `$PSHOME`. If that isn't part of the `PSModulePath` environment variable, an error is written and nothing is returned.

    `Get-PowerShellModuleInstallPath` is new in Carbon 2.0.

    .EXAMPLE
    Get-PowerShellModuleInstallPath

    Demonstrates how to get the path where modules should be installed.
    #>
    [CmdletBinding()]
    [OutputType([string])]
    param(
    )

    Set-StrictMode -Version 'Latest'

    $modulePaths = $env:PSModulePath -split ';'

    $programFileModulePath = Join-Path -Path $env:ProgramFiles -ChildPath 'WindowsPowerShell\Modules'
    $installRoot = $modulePaths | 
                        Where-Object { $_.TrimEnd('\') -eq $programFileModulePath } |
                        Select-Object -First 1
    if( $installRoot )
    {
        return $programFileModulePath
    }

    $psHomeModulePath = Join-Path -Path $PSHOME -ChildPath 'Modules'

    $installRoot = $modulePaths | 
                        Where-Object { $_.TrimEnd('\') -eq $psHomeModulePath } |
                        Select-Object -First 1
    if( $installRoot )
    {
        return $psHomeModulePath
    }

    Write-Error -Message ('PSModulePaths ''{0}'' and ''{1}'' not found in the PSModulePath environment variable.' -f $programFileModulePath,$psHomeModulePath)
}

$installPath = Get-PowerShellModuleInstallPath
$installPath = Join-Path -Path $installPath -ChildPath 'Silk'

$source = Join-Path -Path $PSScriptRoot -ChildPath '..\Silk' -Resolve
if( -not $source )
{
    return
}

if( (Test-Path -Path $installPath -PathType Container) )
{
    $newName = 'Silk{0}' -f [IO.Path]::GetRandomFileName()
    Write-Verbose ('Renaming existing Silk module: {0} -> {1}' -f $installPath,$newName)
    Rename-Item -Path $installPath $newName
    $oldModulePath = Join-Path -Path (Get-PowerShellModuleInstallPath) -ChildPath $newName
    if( Test-Path -Path $oldModulePath -PathType Container )
    {
        Write-Verbose ('Removing old Silk module: {0}' -f $oldModulePath)
        Remove-Item -Path $oldModulePath -Force -Recurse
    }
    else
    {
        return
    }

    if( Test-Path -Path $oldModulePath -PathType Container )
    {
        return
    }
}

Write-Verbose -Message ('Installing Silk: {0} -> {1}' -f $source,$installPath)
Copy-Item -Path $source -Destination $installPath -Recurse
Silk\Functions\Use-CallerPreference.ps1
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#     http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

function Use-CallerPreference
{
    <#
    .SYNOPSIS
    Sets the PowerShell preference variables in a module's function based on the callers preferences.

    .DESCRIPTION
    Script module functions do not automatically inherit their caller's variables, including preferences set by common parameters. This means if you call a script with switches like `-Verbose` or `-WhatIf`, those that parameter don't get passed into any function that belongs to a module. 

    When used in a module function, `Use-CallerPreference` will grab the value of these common parameters used by the function's caller:

     * ErrorAction
     * Debug
     * Confirm
     * InformationAction
     * Verbose
     * WarningAction
     * WhatIf
    
    This function should be used in a module's function to grab the caller's preference variables so the caller doesn't have to explicitly pass common parameters to the module function.

    This function is adapted from the [`Get-CallerPreference` function written by David Wyatt](https://gallery.technet.microsoft.com/scriptcenter/Inherit-Preference-82343b9d).

    There is currently a [bug in PowerShell](https://connect.microsoft.com/PowerShell/Feedback/Details/763621) that causes an error when `ErrorAction` is implicitly set to `Ignore`. If you use this function, you'll need to add explicit `-ErrorAction $ErrorActionPreference` to every function/cmdlet call in your function. Please vote up this issue so it can get fixed.

    .LINK
    about_Preference_Variables

    .LINK
    about_CommonParameters

    .LINK
    https://gallery.technet.microsoft.com/scriptcenter/Inherit-Preference-82343b9d

    .LINK
    http://powershell.org/wp/2014/01/13/getting-your-script-module-functions-to-inherit-preference-variables-from-the-caller/

    .EXAMPLE
    Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    Demonstrates how to set the caller's common parameter preference variables in a module function.
    #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        #[Management.Automation.PSScriptCmdlet]
        # The module function's `$PSCmdlet` object. Requires the function be decorated with the `[CmdletBinding()]` attribute.
        $Cmdlet,

        [Parameter(Mandatory = $true)]
        [Management.Automation.SessionState]
        # The module function's `$ExecutionContext.SessionState` object.  Requires the function be decorated with the `[CmdletBinding()]` attribute. 
        #
        # Used to set variables in its callers' scope, even if that caller is in a different script module.
        $SessionState
    )

    Set-StrictMode -Version 'Latest'

    # List of preference variables taken from the about_Preference_Variables and their common parameter name (taken from about_CommonParameters).
    $commonPreferences = @{
                              'ErrorActionPreference' = 'ErrorAction';
                              'DebugPreference' = 'Debug';
                              'ConfirmPreference' = 'Confirm';
                              'InformationPreference' = 'InformationAction';
                              'VerbosePreference' = 'Verbose';
                              'WarningPreference' = 'WarningAction';
                              'WhatIfPreference' = 'WhatIf';
                          }

    foreach( $prefName in $commonPreferences.Keys )
    {
        $parameterName = $commonPreferences[$prefName]

        # Don't do anything if the parameter was passed in.
        if( $Cmdlet.MyInvocation.BoundParameters.ContainsKey($parameterName) )
        {
            continue
        }

        $variable = $Cmdlet.SessionState.PSVariable.Get($prefName)
        # Don't do anything if caller didn't use a common parameter.
        if( -not $variable )
        {
            continue
        }

        if( $SessionState -eq $ExecutionContext.SessionState )
        {
            Set-Variable -Scope 1 -Name $variable.Name -Value $variable.Value -Force -Confirm:$false -WhatIf:$false
        }
        else
        {
            $SessionState.PSVariable.Set($variable.Name, $variable.Value)
        }
    }

}
Silk\Functions\Test-ModuleVersion.ps1

function Assert-ModuleVersion
{
    param(
        [Parameter(Mandatory=$true)]
        [string]
        # The path to the module's manifest.
        $ManifestPath,

        [string[]]
        # Path to any additional assemblies whose versions should get checked.
        $AssemblyPath,

        [string]
        # Path to a release notes file.
        $ReleaseNotesPath,

        [string]
        # The path to the module's nuspec file.
        $NuspecPath,

        [string[]]
        # A list of assembly file names that should be excluded from the version check. Wildcards allowed. Only assembly names are matched
        $ExcludeAssembly
    )

    Set-StrictMode -Version 'Latest'

    $errorsAtStart = $Error.Count

    $manifest = Test-ModuleManifest -Path $ManifestPath
    if( -not $manifest )
    {
        return
    }

    $version = $manifest.Version

    Write-Verbose -Message ('Checking that {0} module is at version {1}.' -f $manifest.Name,$version)

    $badAssemblies = Invoke-Command {
                            $manifest.RequiredAssemblies | 
                                ForEach-Object { 
                                    if( -not [IO.Path]::IsPathRooted($_) )
                                    {
                                        Join-Path -Path (Split-Path -Parent -Path $manifest.Path) -ChildPath $_
                                    }
                                    else
                                    {
                                        $_
                                    }
                                }
                            if( $AssemblyPath )
                            {
                                $AssemblyPath
                            }
                        } |
                        Where-Object { 
                            foreach( $exclusion in $ExcludeAssembly )
                            {
                                if( (Split-Path -Leaf -Path $_) -like $exclusion )
                                {
                                    return $false
                                }
                            }
                            return $true
                        } |
                        Get-Item | 
                        Where-Object { 
                            -not ($_.VersionInfo.FileVersion.ToString().StartsWith($version.ToString())) -or -not ($_.VersionInfo.ProductVersion.ToString().StartsWith($version.ToString()))
                        } |
                        ForEach-Object {
                            ' * {0} (FileVersion: {1}; ProductVersion: {2})' -f $_.Name,$_.VersionInfo.FileVersion,$_.VersionInfo.ProductVersion
                        }
    if( $badAssemblies )
    {
        Write-Error -Message ('The following assemblies are not at version {0}.{1}{2}' -f $version,([Environment]::NewLine),($badAssemblies -join ([Environment]::NewLine)))
    }

    if( $ReleaseNotesPath )
    {
        $foundFirstVersion = $false
        $releaseNotesVersion = Get-Content -Path $ReleaseNotesPath |
                                    ForEach-Object {
                                        if( -not $foundFirstVersion -and $_ -match '^#\s+(\d+\.\d+\.\d+)' )
                                        {
                                            $foundFirstVersion = $true
                                            return [Version]$Matches[1]
                                        }
                                    }
        if( -not $releaseNotesVersion )
        {
            Write-Error -Message ('Version {0} not found in release notes ({1}).' -f $version,$ReleaseNotesPath)
        }
    }

    if( $NuspecPath )
    {
        $nuspec = [xml](Get-Content -Raw -Path $NuspecPath)
        if( $nuspec )
        {
            $nuspecVersion = [Version]($nuspec.package.metadata.version)
            if( $nuspecVersion )
            {
                if( $version -ne $nuspecVersion )
                {
                    Write-Error -Message ('Nuspec file ''{0}'' is at version {1}, but should be at version {2}..' -f $NuspecPath,$nuspecVersion,$version)
                }
            }
            else
            {
                Write-Error -Message ('Nuspec file ''{0}'' contains an invalid version.' -f $NuspecPath)
            }
        }
        else
        {
            Write-Error -Message ('Nuspec file ''{0}'' does not contain valid XML.' -f $NuspecPath)
        }
    }

    return (($Error.Count - $errorsAtStart) -eq 0)
}
Silk\Functions\Split-MarkdownTopic.ps1

function Split-MarkdownTopic
{
    <#
    .SYNOPSIS
    Parses a Markdown-formatted module help topic, e.g. about_Module.

    .DESCRIPTION
    A Markdown-formatted help topic should contain four sections, `Topic`, `Short Description`, `Long Description`, and `See Also`.  These should all be formatted as level-1 headings.  For example:

        # Topic

        about_Silk

        # Short Description

        Silk is a PowerShell module used to convert another module's help system into an HTML website.

        # Long Description

        MOre details here.  Yadda, yadda, yadda.

        # See Also

        about_Silk_AdditionalTopic
    #>
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        # The root where the config file was found.
        $ConfigFileRoot,

        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        # An object containing information about the topic to parse.
        $TopicInfo
    )
    process
    {
        Set-StrictMode -Version Latest

        $path = $TopicInfo.Path
        if( -not (Test-Path -Path $path -PathType Leaf) )
        {
            Write-Error ('Markdown help topic <{0}> not found.' -f $path)
            return
        }

        $content = New-Object Collections.ArrayList
        $sectionName = $null
        $eof = [Guid]::NewGuid().ToString()
        $topic = New-Object PsObject -Property @{ Name = ''; Synopsis = ''; Description = ''; RelatedLinks = ''; FileName = $TopicInfo.FileName }
        $lineNum = 0
        Invoke-Command { Get-Content -Path $path ; $eof } | ForEach-Object {
            if( $_ -match '^# (.*)$' -or $_ -eq $eof )
            {
                if( $sectionName -or $_ -eq $eof )
                {
                    $topic.$sectionName = $content -join "`n"
                    $topic.$sectionName = $topic.$sectionName.Trim()
                    if( $_ -eq $eof )
                    {
                        return
                    }
                    $content.Clear()
                }


                $sectionName = $matches[1]
                switch -Regex ($sectionName)
                {
                    'Topic|Name' 
                    {
                        $sectionName = 'Name'
                    }
                    'Short Description|Synopsis'
                    {
                        $sectionName = 'Synopsis'
                    }
                    'Long Description|Description'
                    {
                        $sectionName = 'Description'
                    }
                    'See Also|(Related )?Links?'
                    {
                        $sectionName = 'RelatedLinks'
                    }
                    default
                    {
                        Write-Error ('{0}: line {1}: Unknown top-level heading <{2}>.  Expected <Name>, <Synopsis>, <Description>, or <Link>. <Link> may be used multiple times.' -f $path,$lineNum,$_)
                    }
                }
            }
            else
            {
                if( -not $sectionName )
                {
                    Write-Error ('{0}: line {1}: Invalid Markdown help topic: the first line must be `# Name`.' -f $path,$lineNum)
                    return
                }
                [void] $content.Add( $_ )
            }
            ++$lineNum
        }

        if( $TopicInfo | Get-Member Title )
        {
            $topic.Name = $TopicInfo.Title
        }
        return $topic
    }
}
Silk\Functions\Publish-ChocolateyPackage.ps1
# Copyright 2012 Aaron Jensen
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#     http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

function Publish-ChocolateyPackage
{
    <#
    .SYNOPSIS
    Creates and publishes a NuGet package to nuget.org.
    #>
    [CmdletBinding(SupportsShouldProcess=$true)]
    param(
        [Parameter(Mandatory=$true)]
        [string]
        # The path to the module manifest of the module you want to publish.
        $NupkgPath,

        [string]
        # The API key(s) to use. To supply multiple API keys, use a hashtable where each key is a repository server name and the value is the API key for that repository. For example,
        #
        # @{ 'nuget.org' = '395edfa5-652f-4598-868e-c0a73be02c84' }
        #
        # If not specified, you'll be prompted for it. 
        $ApiKey
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState

    if( -not (Get-Command -Name 'choco.exe' -ErrorAction Ignore) )
    {
        Write-Error -Message ('Chocolatey is not installed. Go to http://chocolatey.org for installation instructions.')
        return
    }

    if( -not (Test-Path -Path $NupkgPath -PathType Leaf) )
    {
        Write-Error -Message ('Chocolatey package ''{0}'' does not exist.' -f $NupkgPath)
        return
    }

    $nupkgName = [IO.Path]::GetFileNameWithoutExtension($NupkgPath)
    if( $nupkgName -notmatch '^(.+)\.(\d+\.\d+\.\d)+$' )
    {
        Write-Error -Message ('Chocolatey package ''{0}'' does not have the version to publish in its name.' -f $nupkgName)
        return
    }

    $packageName = $Matches[1]
    $version = $Matches[2]
    try
    {
        $packageUrl = 'https://chocolatey.org/api/v2/package/{0}/{1}' -f $packageName,$version
        try
        {
            $resp = Invoke-WebRequest -Uri $packageUrl -ErrorAction Ignore
            $publish = ($resp.StatusCode -ne 200)
        }
        catch
        {
            $publish = $true
        }

        if( -not $publish )
        {
            Write-Warning ('Chocolatey package {0} {1} already published to chocolatey.org.' -f $packageName,$version)
            return
        }

        if( $PSCmdlet.ShouldProcess(('publish package to chocolatey.org'),'','') )
        {
            if( -not $ApiKey )
            {
                $ApiKey = Read-Host -Prompt ('Please enter your chocolatey.org API key')
                if( -not $ApiKey )
                {
                    Write-Error -Message ('The chocolatey.org API key is required. Package not published to chocolatey.org')
                    continue
                }
            }

            $verbosity = ''
            if( $VerbosePreference -eq 'Continue' )
            {
                $verbosity = '-v'
            }

            choco.exe push --source 'https://chocolatey.org' --key $ApiKey $verbosity $nupkgPath

            $resp = Invoke-WebRequest -Uri $packageUrl
            $resp | Select-Object -Property 'StatusCode','StatusDescription',@{ Name = 'Uri'; Expression = { $packageUrl }}
        }
    }
    finally
    {
        Pop-Location
    }
}
Silk\Functions\Get-TypeDocumentationLink.ps1
Silk\Functions\Get-ModuleVersion.ps1

function Get-ModuleVersion
{
    param(
        [Parameter(Mandatory=$true)]
        [string]
        # The path to the module's manifest.
        $ManifestPath
    )

    Set-StrictMode -Version 'Latest'

}
Silk\Functions\Get-ModuleReleaseNotes.ps1

function Get-ModuleReleaseNotes
{
    param(
        [Parameter(Mandatory=$true)]
        [string]
        # Path to the module's manifest.
        $ManifestPath,

        [Parameter(Mandatory=$true)]
        [string]
        # Path to the releaes notes file.
        $ReleaseNotesPath
    )

    Set-StrictMode -Version 'Latest'

    $Version = Test-ModuleManifest -Path $ManifestPath | Select-Object -ExpandProperty 'Version'
    if( -not $Version )
    {
        Write-Error -Message ('Version not found in module manifest ''{0}''.' -f $ManifestPath)
        return
    }

    $foundVersion = $false
    $versionReleaseNotes = Get-Content -Path $ReleaseNotesPath |
                            Where-Object {
                                $line = $_
                                if( -not $foundVersion )
                                {
                                    if( $line -match ('^#\s+{0}' -f [regex]::Escape($version)) )
                                    {
                                        $foundVersion = $true
                                        return
                                    }
                                }
                                else
                                {
                                    if( $line -match ('^#\s+(?!{0})' -f [regex]::Escape($version)) )
                                    {
                                        $foundVersion = $false
                                    }
                                }
                                return( $foundVersion )
                            }
    if( -not $versionReleaseNotes )
    {
        Write-Error -Message ('There are no release notes for version {0} in ''{1}''.' -f $Version,$ReleaseNotesPath)
        return
    }

    $versionReleaseNotes -join [Environment]::NewLine

}

Log in or click on link to see number of positives.

In cases where actual malware is found, the packages are subject to removal. Software sometimes has false positives. Moderators do not necessarily validate the safety of the underlying software, only that a package retrieves software from the official distribution point and/or validate embedded software against official distribution point (where distribution rights allow redistribution).

Chocolatey Pro provides runtime protection from possible malware.

Add to Builder Version Downloads Last Updated Status

  • Improved about_Silk help topic.
  • Added example scripts Invoke-Build.ps1, Publish-Module.ps1, and New-Website.ps1 to show how to use Silk.

This package has no dependencies.

Discussion for the Silk Package

Ground Rules:

  • This discussion is only about Silk and the Silk package. If you have feedback for Chocolatey, please contact the Google Group.
  • This discussion will carry over multiple versions. If you have a comment about a particular version, please note that in your comments.
  • The maintainers of this Chocolatey Package will be notified about new comments that are posted to this Disqus thread, however, it is NOT a guarantee that you will get a response. If you do not hear back from the maintainers after posting a message below, please follow up by using the link on the left side of this page or follow this link to contact maintainers. If you still hear nothing back, please follow the package triage process.
  • Tell us what you love about the package or Silk, or tell us what needs improvement.
  • Share your experiences with the package, or extra configuration or gotchas that you've found.
  • If you use a url, the comment will be flagged for moderation until you've been whitelisted. Disqus moderated comments are approved on a weekly schedule if not sooner. It could take between 1-5 days for your comment to show up.
comments powered by Disqus