Step 1 of 5 · Reading · ~4 min
Read
Production Concerns
Putting It All Together
Every piece you have written so far handled one construct in isolation. This is where they become a program: one function that takes a document and returns HTML, with each handler you built now living as a branch of a single block dispatcher and a single inline scanner. Your program will convert a whole document, including quotes inside lists inside quotes.
What the finished converter contains
| Phase | Handlers |
|---|---|
| Block, in dispatch order | fenced code, ATX heading, thematic break, blockquote, list, paragraph (which may turn into a setext heading) |
| Inline, one left-to-right scan | backslash escape, code span, image, link, entity or HTML escape |
| Inline, then passes | strong and em (stars), strikethrough (tildes), restore |
The block table is ordered, and so is the inline one: each order exists because an earlier rule would otherwise steal a line, or a later rule would otherwise walk into HTML that an earlier rule produced.
Fenced code content leaves the block phase and goes straight to the renderer, never passing through inline parsing. That is what keeps *not italic* literal inside a fence.
Why does dispatch order decide correctness?
Three lines can each be claimed by two handlers:
| Line | Competing readings | Winner |
|---|---|---|
--- under paragraph text | setext H2 underline, or thematic break | the underline, so the paragraph handler looks ahead |
* * * | thematic break, or a bullet item | the break, so it is tested before lists |
| a line inside an open fence | anything at all | the fence: nothing else gets to see it |
Same input, same rules, different order, different document: a heading turns into a paragraph plus a rule, or a list swallows a break. The paragraph handler goes last because a paragraph is defined as "whatever nothing else claimed".
Containers are recursion
A block quote or list item is handled the way the nesting lesson taught: collect the container's lines, remove its marker, and run the same function on what is left. The converter has one new problem beyond that: tight versus loose is a property of a whole list, but each item is rendered separately. Collect all items first, run each item once only to ask "did a blank line separate two of your blocks?", decide for the list, then render every item for real with the right setting. A blank line between two items also makes the list loose; one after the last item does not.
Two details keep the output right. A tight item writes its paragraph without <p>, while a block quote inside an item goes back to normal paragraphs. And a quote's lazy continuation is allowed only while its last line left a paragraph open.
Inline: one scan, then passes
The inline phase scans the paragraph text once. Whenever a construct is finished (an escaped character, a code span, a link, an image) its HTML goes into the stash behind a placeholder; every other character is escaped on the spot; * and ~ are left alone. Link text goes back through the whole inline phase (that is how [*em*](/u) works), but a link may never contain a link. Then the star and tilde passes pair delimiters, and the placeholders are swapped back.
What your parser still does not do
Link reference definitions, hard line breaks, indented code blocks, autolinks and raw HTML (here a < is always escaped), tables, and the full delimiter-stack emphasis algorithm. CommonMark ships 652 conformance examples in version 0.31.2 because that list is long. cmark is the reference implementation in C; markdown-it, goldmark, pulldown-cmark and markdig are the widely used ports. Use one of those in production. Write your own to understand parsing, which is what you just did.
Common mistakes
- Skipping setext headings. Handle only ATX and
Titleover-----comes out as a paragraph plus an<hr />. - Running inline rules inside a fence. Block content must reach the output untouched except for escaping.
- Deciding tight or loose per item. The whole list is wrapped or none of it is.
- Forgetting that a link's text is inline content too.
Your exercise
Write inline and render_blocks. The starter gives you the recognisers and helpers from every earlier lesson (headings, fences, markers, entities, code spans, link tails, delimiter passes, the stash) so that what remains is the integration itself. Output is the HTML for the whole document.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…