Skip to content

🚀 [Minor]: Markdown documents can now be parsed into objects and regenerated - #18

Draft
Marius Storhaug (MariusStorhaug) wants to merge 6 commits into
mainfrom
feature/8-convert-from-to-markdown
Draft

🚀 [Minor]: Markdown documents can now be parsed into objects and regenerated#18
Marius Storhaug (MariusStorhaug) wants to merge 6 commits into
mainfrom
feature/8-convert-from-to-markdown

Conversation

@MariusStorhaug

Copy link
Copy Markdown
Member

Markdown documents can now be parsed into a structured, AST-like object tree and converted back to well-formatted markdown strings. This enables programmatic inspection, transformation, and round-tripping of markdown content using the standard PowerShell ConvertFrom-/ConvertTo- verb pattern — the same convention used by ConvertFrom-Json and ConvertTo-Json.

New: Parse markdown into structured objects

ConvertFrom-Markdown accepts a markdown string (via -InputObject or pipeline) and returns a MarkdownDocument object tree where each element is a typed node.

$markdown=Get-Content-Raw '.\README.md'$doc=$markdown|ConvertFrom-Markdown$doc.Content# Top-level elements$doc.Content[0].Title # First heading title# Find all headings$doc.Content|Where-Object { $_-is [MarkdownHeader] } |ForEach-Object { $_.Title }

Supported node types: MarkdownHeader (levels 1–6 with nested content), MarkdownCodeBlock (fenced code blocks with language), MarkdownParagraph (with optional <p> tags), MarkdownTable (rows as PSCustomObjects), MarkdownDetails (collapsible <details> sections), and MarkdownText (plain text).

Note: PowerShell 6.1+ includes a built-in ConvertFrom-Markdown cmdlet that converts to HTML/VT100. This module's function shadows it when loaded. The built-in remains accessible via Microsoft.PowerShell.Utility\ConvertFrom-Markdown.

New: Generate markdown from object trees

ConvertTo-Markdown accepts a MarkdownDocument object and renders it back to a markdown string.

$doc=Get-Content-Raw '.\example.md'|ConvertFrom-Markdown$result=$doc|ConvertTo-Markdown$result|Set-Content'.\output.md'

Documents can also be constructed programmatically:

$doc= [MarkdownDocument]::new()
$doc.Content+= [MarkdownHeader]::new(1,'Hello')
$doc.Content+= [MarkdownText]::new('World')
ConvertTo-Markdown-InputObject $doc

New: Round-trip markdown preservation

Parsing a markdown string and converting it back preserves the structural content of the document:

$original=Get-Content-Raw '.\doc.md'$roundTripped=$original|ConvertFrom-Markdown|ConvertTo-Markdown

Technical Details

  • Class hierarchy in src/classes/: MarkdownDocument, MarkdownHeader, MarkdownCodeBlock, MarkdownParagraph, MarkdownTable, MarkdownDetails, MarkdownText. Uses PowerShell classes for type safety and IntelliSense. No Parent references (avoids circular serialization issues).
  • Parser (ConvertFrom-Markdown): Line-by-line state machine using a stack to track container nesting. Handles heading hierarchy (auto-nests by level), fenced code blocks, <details>/<summary> blocks with nested <p> structural tags, pipe-delimited tables, and <p>-tagged paragraphs.
  • Renderer (ConvertTo-Markdown): Recursive node visitor using StringBuilder. Each node type has its own rendering logic.
  • Tests: 28 new Pester tests across ConvertFrom-Markdown (13 tests), ConvertTo-Markdown (10 tests), and round-trip integration (5 tests). Covers all node types, nesting, edge cases (empty documents, level jumps), and pipeline input.
  • Relationship to DSL: The Set-Markdown* functions (DSL approach) and the ConvertFrom-/ConvertTo- functions (object approach) are complementary and independent. The DSL remains the imperative way to compose markdown; the Convert functions add parse/serialize capabilities.
  • Informed by the community prototype in PR Add option to convert from markdown and to convert to DSL #14 (ConvertFrom-MarkdownMarkdown/ConvertTo-MarkdownDSL) but significantly redesigned: proper class hierarchy instead of PSCustomObjects, string input instead of file paths, markdown output instead of DSL script blocks.

@github-actions

Copy link
Copy Markdown

Super-linter summary

LanguageValidation result
CHECKOVPass ✅
GITHUB_ACTIONSPass ✅
GITLEAKSPass ✅
GIT_MERGE_CONFLICT_MARKERSPass ✅
JSONPass ✅
MARKDOWNPass ✅
NATURAL_LANGUAGEFail ❌
POWERSHELLFail ❌
PRE_COMMITPass ✅
SPELL_CODESPELLPass ✅
TRIVYPass ✅
YAMLPass ✅

Super-linter detected linting errors

For more information, see the GitHub Actions workflow run

Powered by Super-linter

NATURAL_LANGUAGE

/github/workspace/README.md
325:111 ✓ error Incorrect term: “markdown”, use “Markdown” instead terminology
330:10 ✓ error Incorrect term: “markdown”, use “Markdown” instead terminology
345:74 ✓ error Incorrect term: “markdown”, use “Markdown” instead terminology
346:1 ✓ error Incorrect term: “markdown”, use “Markdown” instead terminology
✖ 4 problems (4 errors, 0 warnings, 0 infos)
✓ 4 fixable problems.
Try to run: $ textlint --fix [file]
POWERSHELL

�[32;1mRuleName �[0m�[32;1m Severity �[0m�[32;1m ScriptName�[0m�[32;1m Line �[0m�[32;1m Message�[0m
�[32;1m-------- �[0m �[32;1m-------- �[0m �[32;1m----------�[0m �[32;1m---- �[0m �[32;1m-------�[0m
PSAvoidOverwritingBuiltInCmdlets Warning ConvertFro 1 'ConvertFrom-
m-Markdown Markdown' is
.ps1 a cmdlet that
is included
with PowerShe
ll (version c
ore-6.1.0-win
dows) whose d
efinition sho
uld not be ov
erridden
�[32;1mRuleName �[0m�[32;1m Severity �[0m�[32;1m ScriptName�[0m�[32;1m Line �[0m�[32;1m Message�[0m
�[32;1m-------- �[0m �[32;1m-------- �[0m �[32;1m----------�[0m �[32;1m---- �[0m �[32;1m-------�[0m
PSUseSingularNouns Warning ConvertTo- 45 The cmdlet 'W
Markdown.p rite-Nodes' u
s1 ses a plural
noun. A singu
lar noun shou
ld be used in
stead.
PSProvideCommentHelp Information ConvertTo- 45 The cmdlet 'W
Markdown.p rite-Nodes' d
s1 oes not have
a help commen
t.

@github-actions

Copy link
Copy Markdown

Super-linter summary

LanguageValidation result
CHECKOVPass ✅
GITHUB_ACTIONSPass ✅
GITLEAKSPass ✅
GIT_MERGE_CONFLICT_MARKERSPass ✅
JSONPass ✅
MARKDOWNPass ✅
NATURAL_LANGUAGEFail ❌
POWERSHELLFail ❌
PRE_COMMITPass ✅
SPELL_CODESPELLPass ✅
TRIVYPass ✅
YAMLPass ✅

Super-linter detected linting errors

For more information, see the GitHub Actions workflow run

Powered by Super-linter

NATURAL_LANGUAGE

/github/workspace/README.md
325:111 ✓ error Incorrect term: “markdown”, use “Markdown” instead terminology
330:10 ✓ error Incorrect term: “markdown”, use “Markdown” instead terminology
345:74 ✓ error Incorrect term: “markdown”, use “Markdown” instead terminology
346:1 ✓ error Incorrect term: “markdown”, use “Markdown” instead terminology
✖ 4 problems (4 errors, 0 warnings, 0 infos)
✓ 4 fixable problems.
Try to run: $ textlint --fix [file]
POWERSHELL

�[32;1mRuleName �[0m�[32;1m Severity �[0m�[32;1m ScriptName�[0m�[32;1m Line �[0m�[32;1m Message�[0m
�[32;1m-------- �[0m �[32;1m-------- �[0m �[32;1m----------�[0m �[32;1m---- �[0m �[32;1m-------�[0m
PSAvoidOverwritingBuiltInCmdlets Warning ConvertFro 1 'ConvertFrom-
m-Markdown Markdown' is
.ps1 a cmdlet that
is included
with PowerShe
ll (version c
ore-6.1.0-win
dows) whose d
efinition sho
uld not be ov
erridden
�[32;1mRuleName �[0m�[32;1m Severity �[0m�[32;1m ScriptName�[0m�[32;1m Line �[0m�[32;1m Message�[0m
�[32;1m-------- �[0m �[32;1m-------- �[0m �[32;1m----------�[0m �[32;1m---- �[0m �[32;1m-------�[0m
PSProvideCommentHelp Information ConvertTo- 45 The cmdlet 'W
Markdown.p rite-Nodes' d
s1 oes not have
a help commen
t.
PSUseSingularNouns Warning ConvertTo- 45 The cmdlet 'W
Markdown.p rite-Nodes' u
s1 ses a plural
noun. A singu
lar noun shou
ld be used in
stead.

@github-actions

Copy link
Copy Markdown

Super-linter summary

LanguageValidation result
CHECKOVPass ✅
GITHUB_ACTIONSPass ✅
GITLEAKSPass ✅
GIT_MERGE_CONFLICT_MARKERSPass ✅
MARKDOWNPass ✅
NATURAL_LANGUAGEFail ❌
POWERSHELLPass ✅
PRE_COMMITPass ✅
SPELL_CODESPELLPass ✅
TRIVYPass ✅
YAMLPass ✅

Super-linter detected linting errors

For more information, see the GitHub Actions workflow run

Powered by Super-linter

NATURAL_LANGUAGE

/github/workspace/README.md
325:111 ✓ error Incorrect term: “markdown”, use “Markdown” instead terminology
330:10 ✓ error Incorrect term: “markdown”, use “Markdown” instead terminology
345:74 ✓ error Incorrect term: “markdown”, use “Markdown” instead terminology
346:1 ✓ error Incorrect term: “markdown”, use “Markdown” instead terminology
✖ 4 problems (4 errors, 0 warnings, 0 infos)
✓ 4 fixable problems.
Try to run: $ textlint --fix [file]

@github-actions

Copy link
Copy Markdown

Super-linter summary

LanguageValidation result
CHECKOVPass ✅
GITHUB_ACTIONSPass ✅
GITLEAKSPass ✅
GIT_MERGE_CONFLICT_MARKERSPass ✅
MARKDOWNPass ✅
NATURAL_LANGUAGEPass ✅
POWERSHELLPass ✅
PRE_COMMITPass ✅
SPELL_CODESPELLPass ✅
TRIVYPass ✅
YAMLPass ✅

All files and directories linted successfully

For more information, see the GitHub Actions workflow run

Powered by Super-linter

@github-actions

Copy link
Copy Markdown

Super-linter summary

LanguageValidation result
CHECKOVPass ✅
GITHUB_ACTIONSPass ✅
GITLEAKSPass ✅
GIT_MERGE_CONFLICT_MARKERSPass ✅
MARKDOWNPass ✅
NATURAL_LANGUAGEPass ✅
POWERSHELLPass ✅
PRE_COMMITPass ✅
SPELL_CODESPELLPass ✅
TRIVYPass ✅
YAMLPass ✅

All files and directories linted successfully

For more information, see the GitHub Actions workflow run

Powered by Super-linter

@github-actions

Copy link
Copy Markdown

Super-linter summary

LanguageValidation result
CHECKOVPass ✅
GITHUB_ACTIONSPass ✅
GITLEAKSPass ✅
GIT_MERGE_CONFLICT_MARKERSPass ✅
MARKDOWNPass ✅
NATURAL_LANGUAGEPass ✅
POWERSHELLPass ✅
PRE_COMMITPass ✅
SPELL_CODESPELLPass ✅
TRIVYPass ✅
YAMLPass ✅

All files and directories linted successfully

For more information, see the GitHub Actions workflow run

Powered by Super-linter

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a specification-compliant markdown object hierarchy with ConvertFrom-Markdown and ConvertTo-Markdown

1 participant

@MariusStorhaug