MarkX.types.ps1xml

<!-- Generated with EZOut 2.0.6: Install-Module EZOut or https://github.com/StartAutomating/EZOut -->
<Types>
  <Type>
    <Name>MarkX</Name>
    <Members>
      <MemberSet>
        <Name>PSStandardMembers</Name>
        <Members>
          <PropertySet>
            <Name>DefaultDisplayPropertySet</Name>
            <ReferencedProperties>
              <Name>Markdown</Name>
              <Name>XML</Name>
            </ReferencedProperties>
          </PropertySet>
        </Members>
      </MemberSet>
      <AliasProperty>
        <Name>DB</Name>
        <ReferencedMemberName>DataSet</ReferencedMemberName>
      </AliasProperty>
      <AliasProperty>
        <Name>Heading</Name>
        <ReferencedMemberName>Headings</ReferencedMemberName>
      </AliasProperty>
      <AliasProperty>
        <Name>Link</Name>
        <ReferencedMemberName>Links</ReferencedMemberName>
      </AliasProperty>
      <AliasProperty>
        <Name>Tables</Name>
        <ReferencedMemberName>Table</ReferencedMemberName>
      </AliasProperty>
      <ScriptMethod>
        <Name>Sync</Name>
        <Script>
                        $currentRows = @()
$allMarkdown = @(:nextInput foreach ($md in $this.Input) {
    if ($md -isnot [string]) {
        # If the markdown was a file
        if ($md -is [IO.FileInfo] -and
            # and it had the extension .md or markdown
            $md.Extension -in '.md', '.markdown') {
            $md = Get-Content -LiteralPath $md.Fullname -Raw
            $md
            continue
        }
        
        if ($md -is [Management.Automation.CommandInfo]) {
            $cmdHelp = if (
                $md -is [Management.Automation.FunctionInfo] -or
                $md -is [Management.Automation.AliasInfo]
            ) {
                Get-Help -Name $md.Name
            } elseif ($md -is [Management.Automation.ExternalScriptInfo]) {
                Get-Help -Name $md.Source
            } else {
                continue nextInput
            }
            if ($cmdHelp) {
                $md = $cmdHelp
            }
        }

        if ($md.pstypenames -match 'HelpInfo') {
            @(
                if ($md.Name -match '[\\/]') {
                    "# $(@($md.Name -split '[\\/]')[-1] -replace '\.ps1')"
                } else {
                    "# $($md.Name)"
                }
                
                if ($md.Synopsis) {
                    "## $($md.Synopsis)"
                }
                $description = $md.Description.text -join [Environment]::NewLine
                if ($description) {
                    "### $($description)"
                }
                
                $md.alertset.alert.text -join [Environment]::NewLine
                
                foreach ($example in $md.examples.example) {
                    $exampleNumber++
                    
                    # Combine the code and remarks
                    $exampleLines =
                        @(
                            $example.Code
                            foreach ($remark in $example.Remarks.text) {
                                if (-not $remark) { continue }
                                $remark
                            }
                        ) -join ([Environment]::NewLine) -split '(?&gt;\r\n|\n)' # and split into lines

                    # Anything until the first non-comment line is a markdown predicate to the example
                    $nonCommentLine = $false
                    $markdownLines = @()

                    # Go thru each line in the example as part of a loop
                    $codeBlock = @(foreach ($exampleLine in $exampleLines) {
                        # Any comments until the first uncommentedLine are markdown
                        if ($exampleLine -match '^\#' -and -not $nonCommentLine) {
                            $markdownLines += $exampleLine -replace '^\#' -replace '^\s+'
                        } else {
                            $nonCommentLine = $true
                            $exampleLine
                        }
                    }) -join [Environment]::NewLine
                    
                    # Join all of our markdown lines together
                    $markdownLines -join [Environment]::NewLine
                    "~~~PowerShell"
                    $codeBlock
                    "~~~"
                }
            ) -join [Environment]::NewLine
            continue nextInput
        }
        if ($md -is [ScriptBlock]) {
            "&lt;pre&gt;&lt;code class='language-powershell'&gt;$(
                [Web.HttpUtility]::HtmlEncode(
                    "$md"
                )
            )&lt;/code&gt;&lt;pre&gt;"
            continue nextInput
        }
        if ($md -is [Collections.IDictionary] -or
            ($md.GetType -and
                (-not $md.GetType().IsPrimitive)
            )
        ) {
            $currentRows += $md
            continue
        }
    }
    
    if ($currentRows) {
        $this.ToTable($currentRows)
        $currentRows = @()
    }

    if ($md -match '(?&gt;\.md|markdown)$' -and
        (Test-Path $md -ErrorAction Ignore)
    ) {
        $md = Get-Content -Raw $md
    }

    $yamlheader = ''
    if ($md -match '^---') {
        $null, $yamlheader, $md = $in -split '---', 3
        if ($yamlheader) {
            $this | Add-Member NoteProperty '#YamlHeader' $yamlheader -Force
        }
    }

    $md
})

if ($currentRows) {
    $allMarkdown += $this.ToTable($currentRows)
    $currentRows = @()
}

$yamlHeaders = @()
$allMarkdown = @(foreach ($md in $allMarkdown) {
    if ($md -match '^---') {
        $null, $yamlheader, $restOfMakdown = $md -split '---', 3
        if ($yamlheader) {
            $yamlHeaders+= $yamlheader
        }
        $restOfMakdown
    } else {
        $md
    }
})

if ($yamlHeaders) {
    $yamlHeader = $yamlHeaders -join (
        [Environment]::NewLine + '---' + [Environment]::NewLine
    )
    $this | Add-Member NoteProperty '#YamlHeader' $yamlHeader -Force
}

$markdown = $allMarkdown -join [Environment]::NewLine

$this |
    Add-Member NoteProperty '#Markdown' $Markdown -Force

$Markdown = $this.'#Markdown'

if (-not $Markdown) { return }

$this |
    Add-Member NoteProperty '#HTML' (
        $Markdown |
            ConvertFrom-Markdown |
            Select-Object -ExpandProperty Html
    ) -Force

$this |
    Add-Member NoteProperty '#XML' (
        "&lt;xhtml&gt;$($this.'#HTML')&lt;/xhtml&gt;" -as [xml]
    ) -Force

if (-not $this.'#XML') { return }

$tables = $this.'#XML' | Select-Xml //table
filter innerText {
    $in = $_
    if ($in -is [string]) { "$in" }
    elseif ($in.innerText) { "$($in.innerText)" }
    elseif ($in.'#text') { "$($in.'#text')" }
}

function bestType {
    $allIn = @($input) + @(if ($args) { $args})
    switch ($true) {
        { $allIn -as [float[]] } {
            [float]; break
        }
        { $allIn -as [double[]] } {
            [double]; break
        }
        { $allIn -as [long[]] } {
            [long]; break
        }
        { $allIn -as [ulong[]] } {
            [uint32]; break
        }
        { $allIn -as [decimal[]] } {
            [decimal]; break
        }
        { $allIn -as [timespan[]] } {
            [timespan]; break
        }
        { $allIn -as [DateTime[]] } {
            [DateTime]; break
        }
        default {
            [string]
        }
    }
}

$markdownData = [Data.DataSet]::new('MarkX')
$tableNumber = 0
foreach ($table in $tables) {
    $tableNumber++
    $markdownDataTable = $markdownData.Tables.Add("MarkdownTable$tableNumber")
    
    [string[]]$PropertyNames = @( $table.Node.thead.tr.th | innerText )

    # We want to upcast our datatable as much as possible
    # so we need to collect the rows first
    $TableDictionary = [Ordered]@{}
    $propertyIndex = 0
    foreach ($property in $propertyNames) {
        $TableDictionary[$property] = @(
            foreach ($row in $table.Node.tbody.tr) {
                @($row.td)[$propertyIndex] | innerText
            }
        )
        $propertyIndex++
    }

    # Now that we have all of the data collected,
    $markdownDataTable.Columns.AddRange(@(
        foreach ($property in $propertyNames) {
            $propertyIndex = 0
            $bestType = $TableDictionary[$property] | bestType
            [Data.DataColumn]::new($property, $bestType, '', 'Attribute')
        }
        [Data.DataColumn]::new('tr', [xml.xmlelement], '', 'Hidden')
    ))
        
    foreach ($row in $table.Node.tbody.tr) {
        $propertyValues = @(
            $row.td | innerText
            $row
        )
        $null = $markdownDataTable.Rows.Add($propertyValues)
    }

    $previous = $table.Node.PreviousSibling
    if ($previous.LocalName -eq 'blockquote') {
        $markdownDataTable.ExtendedProperties.Add("description", $previous.InnerText)
        $previous = $previous.PreviousSibling
    }
    if ($previous.LocalName -match 'h[1-6]') {
        $markdownDataTable.TableName = $previous.InnerText
    }
}

$this | Add-Member NoteProperty '#DataSet' $markdownData -Force

                    </Script>
      </ScriptMethod>
      <ScriptMethod>
        <Name>ToString</Name>
        <Script>
                        if ($args) {
    $anyOutput = foreach ($arg in $args) {
        $thisArg = $this.$arg
        if ($thisArg) {
            if ($thisArg.XHTML.InnerXML) {
                "$($thisArg.XHTML.InnerXML)" + [Environment]::NewLine
            } else {
                "$thisArg"
            }
        }
    }
    if ($anyOutput) {
        return $anyOutput -join [Environment]::NewLine
    }
}

if (-not $this.XML.XHTML) { return '' }
return ("$($this.XML.XHTML.InnerXML)" + [Environment]::NewLine)
                    </Script>
      </ScriptMethod>
      <ScriptMethod>
        <Name>ToTable</Name>
        <Script>
                        param(
[PSObject[]]
$Rows
)

$allInput = @($input)
$allRows = @($allInput) + $(if ($Rows) {
    $rows
})

$MarkdownLines = @()
$IsFirst = $true
foreach ($in in $Rows) {
    $propertyList = @(
        # we first need to get a list of properties.
        if ($in -is [Collections.IDictionary])
        {
            foreach ($k in $in.Keys) { # take all keys from the dictionary
                $k
            }
        }
        # Otherwise, walk over all properties on the object
        else {
            foreach ($psProp in $In.psobject.properties) {
                $psProp
            }
        }
    )

    # If we're rendering the first row of a table
    if ($IsFirst) {
        # Create the header
        $markdownLines +=
            '|' + (@(foreach ($prop in $propertyList) {
                if ($prop -is [string]) {
                    $prop
                } else {
                    $prop.Name
                }
            }) -replace ([Environment]::newline), '&lt;br/&gt;' -replace '\|', '\|' -join '|') + '|'
        # Then create the alignment row.
        $markdownLines +=
            '|' + $(
                $columnNumber =0
                @(
                    foreach ($prop in $propertyList) {
                        $colLength =
                            if ($prop -is [string]) {
                                $prop.Length
                            } else {
                                $prop.Name.Length
                            }
                        
                        "-" * $colLength
                        
                        
                        $columnNumber++
                    }
                ) -replace ([Environment]::newline), '&lt;br/&gt;' -replace '\|', '\|' -join '|') + '|'
        $IsFirst = $false
    }
    
    # Now we create the row for this object.

    $markdownLine = '|' + (
        @(
            foreach ($prop in $propertyList) {
                if ($prop -is [string]) {
                    $in.$prop
                } else {
                    $prop.Value
                }
            }
        ) -replace ([Environment]::newline), '&lt;br/&gt;' -replace '\|', '\|' -join '|') + '|'

    $markdownLines += $markdownLine
}
$markdownLines
                    </Script>
      </ScriptMethod>
      <ScriptProperty>
        <Name>Code</Name>
        <GetScriptBlock>
                        &lt;#
.SYNOPSIS
    Gets code
.DESCRIPTION
    Gets code within Markdown / MarkX, grouped by language.
.EXAMPLE
    "
    # Hello World in PowerShell
    ~~~PowerShell
    'Hello World'
    ~~~
    " | markx | Select-Object -ExpandProperty Code
#&gt;
$codeByLanguage = [Ordered]@{}

foreach ($element in $this.XML | Select-Xml -XPath //code) {
    $language = 'unknown'
    if ($element.node.class -match 'language-(?&lt;language&gt;\S+)?') {
        $language = $matches.language
    }
    if (-not $codeByLanguage[$language]) {
        $codeByLanguage[$language] = @()
    }
    $codeByLanguage[$language] += $element.node.InnerText
}

return $codeByLanguage
                    </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>CodeBlock</Name>
        <GetScriptBlock>
                        &lt;#
.SYNOPSIS
    Gets code blocks
.DESCRIPTION
    Gets code blocks within Markdown / MarkX
.EXAMPLE
    "
    # Hello World in PowerShell
    ~~~PowerShell
    'Hello World'
    ~~~
    " | markx | Select-Object -ExpandProperty CodeBlock
#&gt;
$codeByLanguage = [Ordered]@{}

foreach ($element in $this.XML | Select-Xml -XPath //code) {
    $language = 'unknown'
    if ($element.node.class -match 'language-(?&lt;language&gt;\S+)?') {
        $language = $matches.language
    }
    if (-not $codeByLanguage[$language]) {
        $codeByLanguage[$language] = @()
    }
    $codeByLanguage[$language] += $element.node
}

return $codeByLanguage
                    </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>DataSet</Name>
        <GetScriptBlock>
                        return $this.'#DataSet'
                    </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>Header</Name>
        <GetScriptBlock>
                        if (-not $this.'#YamlHeader') { return }

$convertFromYaml = $ExecutionContext.SessionState.InvokeCommand.GetCommand('ConvertFrom-Yaml', 'Alias,Cmdlet,Function')
if (-not $convertFromYaml) {
    Write-Warning "Cannot get header without ConvertFrom-Yaml"
    return
}

return ($this.'#YamlHeader' | &amp; $convertFromYaml)
                    </GetScriptBlock>
        <SetScriptBlock>
                        param($header)

$this.YamlHeader = $header
                    </SetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>Headings</Name>
        <GetScriptBlock>
                        &lt;#
.SYNOPSIS
    Gets Markdown headings
.DESCRIPTION
    Gets any heading elements in the markdown
#&gt;
$this.XML |
    Select-Xml -XPath //* |
    Where-Object {
        $_.Node.LocalName -match 'h[1-6]'
    } |
    Select-Object -ExpandProperty Node
                    </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>HTML</Name>
        <GetScriptBlock>
                        if (-not $this.XML.XHTML) { return '' }
return ("$($this.XML.XHTML.InnerXML)" + [Environment]::NewLine)
                    </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>Images</Name>
        <GetScriptBlock>
                        foreach ($aNode in $this.XML | Select-Xml //img) {
    if ($aNode.Node.src) {
        $aNode.Node
    }
}
                    </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>InnerText</Name>
        <GetScriptBlock>
                        $this.XML.XHTML.InnerText
                    </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>Input</Name>
        <GetScriptBlock>
                        return $this.'#input'
                    </GetScriptBlock>
        <SetScriptBlock>
                        param(
[PSObject[]]$InputObject
)

$this | Add-Member NoteProperty '#Input' $InputObject -Force

$this.Sync()
                    </SetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>Lexicon</Name>
        <GetScriptBlock>
                        &lt;#
.SYNOPSIS
    Gets Lexicons from Markdown
.DESCRIPTION
    Gets At Protocol Lexicons defined in Markdown.

    A lexicon table must have at least three columns:
    
    * Property
    * Type
    * Description

    It must also contain a row containing the property `$type`.

    This will be considered the lexicon's type.

    Any bolded or italic fields will be considered required.

    Lexicon tables may also be preceeded by an element containing the description.
#&gt;

$markdownData = $this.DataSet
:nextTable foreach ($table in $markdownData.Tables) {
    $isLexiconTable = $table.Columns['Property'] -and $table.Columns['Type'] -and $table.Columns['Description']

    if (-not $isLexiconTable) { continue nextTable }

    $hasType = $table.Select("Property='`$type'")
    if (-not $hasType) {
        Write-Warning "Missing `$type"
        continue nextTable
    }

    $lexiconType = if ($hasType.Description -match '(?:[^\.\s]+\.){3}[^\.\s]+') {
        $matches.0
    }

    if (-not $lexiconType) {
        continue nextTable
    }

    $lexiconObject = [Ordered]@{
        lexicon = 1
        id = $lexiconType
        defs = @{
            main = [Ordered]@{
                type = 'record'
                description =
                    if ($table.ExtendedProperties.Description) {
                        $table.ExtendedProperties.Description
                    } else {
                        $lexiconType
                    }
                required = @()
                properties = [Ordered]@{}
            }
        }
    }

    foreach ($row in $table) {
        
        if ($row.Property -eq '$type') { continue }
        $lexProp = [Ordered]@{}
        $lexProp.type =
            switch -regex ($row.type) {
                '\[\]' { 'array' }
                'object' { 'object' }
                'string' { 'string' }
                'bool|switch' { 'boolean' }
                'int|number|float|double' { 'number' }
                'date' { 'datetime' }
                'ur[il]' { 'uri'}
            }
            
        $lexProp.description = $row.Description
        if ($row.Property -match '\*') {
            $lexiconObject.defs.main.required += $row.Property -replace '\*'
        }
        elseif ($row.tr.td.outerxml -match '&lt;(?&gt;b|i|strong)&gt;') {
            $lexiconObject.defs.main.required += $row.Property -replace '\*'
        }
        
        $lexiconObject.defs.main.properties[$row.Property -replace '\*'] = $lexProp
    }

    $lexiconObject
}
                    </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>Links</Name>
        <GetScriptBlock>
                        foreach ($aNode in $this.XML | Select-Xml //a) {
    if ($aNode.Node.href) {
        $aNode.Node
    }
}
                    </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>Markdown</Name>
        <GetScriptBlock>
                        return $this.'#Markdown'
                    </GetScriptBlock>
        <SetScriptBlock>
                        param(
[PSObject[]]$Markdown
)

$this.Input = $Markdown

                    </SetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>Table</Name>
        <GetScriptBlock>
                        $this.XML | Select-Xml -XPath '//table' | Select-Object -ExpandProperty Node
                    </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>XML</Name>
        <GetScriptBlock>
                        return $this.'#XMl'
                    </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>YamlHeader</Name>
        <GetScriptBlock>
                        return $this.'#YamlHeader'
                    </GetScriptBlock>
        <SetScriptBlock>
                        param($header)

if ($header -is [string]) {
    $this | Add-Member NoteProperty '#YamlHeader' $header -Force
    return
}

$convertToYaml = $ExecutionContext.SessionState.InvokeCommand.GetCommand('ConvertTo-Yaml', 'Alias,Cmdlet,Function')
if (-not $convertToYaml) {
    Write-Warning "Cannot set yaml header without converter"
    return
}

$convertParameters = @{}
if ($convertToYaml.Parameters['Depth']) {
    $convertParameters['Depth'] = $FormatEnumerationLimit
}
$toYaml = $header | &amp; $convertToYaml @convertParameters
if ($toYaml -is [string]) {
    $this | Add-Member NoteProperty '#YamlHeader' $toYaml -Force
}



                    </SetScriptBlock>
      </ScriptProperty>
      <NoteProperty>
        <Name>DefaultDisplay</Name>
        <Value>Markdown
XML</Value>
      </NoteProperty>
    </Members>
  </Type>
</Types>