The object hierarchy from #8 is designed to be handed to any general-purpose serializer, so a markdown document can be turned into YAML, JSON, or CLIXML with no markdown-specific code:
Get-Content -Raw 'README.md' | ConvertFrom-Markdown | ConvertTo-Yaml | Set-Content 'README.yaml'
Only one direction works. Coming back the other way produces untyped PSCustomObject graphs, not MarkdownNode instances, so ConvertTo-Markdown cannot render them and none of the traversal helpers exist on them.
Request
Desired capability
A way to read a serialized hierarchy back into typed nodes, closing the loop:
$doc = Get-Content -Raw 'README.yaml' | ConvertFrom-Yaml | ConvertTo-MarkdownDocument
$doc.Descendants('Heading') | ForEach-Object { $_.GetText() }
$doc | ConvertTo-Markdown | Set-Content 'README.md'
This makes the hierarchy a genuine interchange format rather than a one-way export. The scenarios it unlocks:
- Edit markdown structure in a format that is easier to manipulate. Export to YAML, transform with any YAML tooling, come back to markdown.
- Store parsed documents. Cache a parse result and reload it without re-parsing.
- Generate markdown from data. Produce the hierarchy as YAML or JSON from a template or another system, then convert it to markdown without ever constructing PowerShell classes by hand.
- Cross-process and cross-language hand-off. Any tool that can emit the documented schema can produce a markdown document.
Acceptance criteria
- A public function reconstructs a typed
MarkdownDocument from a deserialized object graph — the output of ConvertFrom-Yaml, ConvertFrom-Json, or Import-Clixml
- Each node's
Type property selects the class to instantiate
- Nested
Children are reconstructed recursively into typed nodes
- Enum-valued properties are restored from their string names
ConvertFrom-Markdown | ConvertTo-Yaml | ConvertFrom-Yaml | <this function> | ConvertTo-Markdown reproduces the original document
- An unrecognized
Type, a missing required property, or a malformed graph produces a clear error naming the offending node — not a partially built tree
Source spans are restored when present and left $null when absent
Dependencies
- PSModule/Markdown#8 — the object hierarchy, the
Type discriminator, and the node constructors this function calls
Technical decisions
Open: function name. Candidates, none settled:
| Candidate |
For |
Against |
ConvertTo-MarkdownDocument |
Reads naturally in a pipeline; the noun says what comes out |
A second ConvertTo-* verb in a module that already has ConvertTo-Markdown invites confusion |
ConvertFrom-MarkdownObject |
Pairs with ConvertFrom-Markdown |
The direction reads backwards — it converts to the hierarchy |
New-MarkdownDocument |
Standard verb for constructing an object |
Suggests an empty document, not a rehydrated one |
Import-MarkdownDocument |
Clear intent |
Import-* implies reading from a file, which this does not do |
To be resolved before implementation.
Reconstruction is driven by Type: Every node carries a Type string precisely so the serialized form is self-describing. The function maps Type to a class and instantiates it. No type inference from property shape — an explicit discriminator or an error.
Strict by default: An unknown Type or a missing required property is an error, not a silently dropped node. Producing a tree that is quietly incomplete is worse than failing, because the failure surfaces as corrupted markdown much later.
Accepts any deserialized graph: The function takes PSCustomObject and hashtable shapes, so it works with ConvertFrom-Yaml, ConvertFrom-Json, and Import-Clixml alike without a format-specific parameter. The source format is the caller's concern.
The schema is the contract: The node schema documented in #8 becomes a public interface once external tools can produce it. Schema changes after this ships are breaking changes, which is an argument for landing it after 1.3 has settled rather than alongside it.
Release shape: A new public function. Additive, so a minor bump. Sequenced after 1.5, since the dialect node types from #30 should be reconstructable too rather than requiring a second pass over this function.
Implementation plan
Core
Error handling
Tests
Documentation
The object hierarchy from #8 is designed to be handed to any general-purpose serializer, so a markdown document can be turned into YAML, JSON, or CLIXML with no markdown-specific code:
Only one direction works. Coming back the other way produces untyped
PSCustomObjectgraphs, notMarkdownNodeinstances, soConvertTo-Markdowncannot render them and none of the traversal helpers exist on them.Request
Desired capability
A way to read a serialized hierarchy back into typed nodes, closing the loop:
This makes the hierarchy a genuine interchange format rather than a one-way export. The scenarios it unlocks:
Acceptance criteria
MarkdownDocumentfrom a deserialized object graph — the output ofConvertFrom-Yaml,ConvertFrom-Json, orImport-ClixmlTypeproperty selects the class to instantiateChildrenare reconstructed recursively into typed nodesConvertFrom-Markdown | ConvertTo-Yaml | ConvertFrom-Yaml | <this function> | ConvertTo-Markdownreproduces the original documentType, a missing required property, or a malformed graph produces a clear error naming the offending node — not a partially built treeSourcespans are restored when present and left$nullwhen absentDependencies
Typediscriminator, and the node constructors this function callsTechnical decisions
Open: function name. Candidates, none settled:
ConvertTo-MarkdownDocumentConvertTo-*verb in a module that already hasConvertTo-Markdowninvites confusionConvertFrom-MarkdownObjectConvertFrom-MarkdownNew-MarkdownDocumentImport-MarkdownDocumentImport-*implies reading from a file, which this does not doTo be resolved before implementation.
Reconstruction is driven by
Type: Every node carries aTypestring precisely so the serialized form is self-describing. The function mapsTypeto a class and instantiates it. No type inference from property shape — an explicit discriminator or an error.Strict by default: An unknown
Typeor a missing required property is an error, not a silently dropped node. Producing a tree that is quietly incomplete is worse than failing, because the failure surfaces as corrupted markdown much later.Accepts any deserialized graph: The function takes
PSCustomObjectandhashtableshapes, so it works withConvertFrom-Yaml,ConvertFrom-Json, andImport-Clixmlalike without a format-specific parameter. The source format is the caller's concern.The schema is the contract: The node schema documented in #8 becomes a public interface once external tools can produce it. Schema changes after this ships are breaking changes, which is an argument for landing it after 1.3 has settled rather than alongside it.
Release shape: A new public function. Additive, so a minor bump. Sequenced after 1.5, since the dialect node types from #30 should be reconstructable too rather than requiring a second pass over this function.
Implementation plan
Core
src/functions/public/Typeto class map covering every node type in the hierarchyChildrenrecursivelyMarkdownSourceSpanwhen presentPSCustomObjectandhashtablenode shapesError handling
TypeTests
Documentation
README.md