Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

hypermarkdown

A fast, lightweight Markdown-to-HTML converter written in Go.
Built as a learning project — no dependencies, standard library only.


Install

If you have Go installed:

go install github.com/itsjasonpierrot/hypermarkdown@latest

The binary lands in ~/go/bin/. If that directory is on your $PATH, hypermarkdown works from anywhere immediately.

From source

git clone https://github.com/itsjasonpierrot/hypermarkdown
cd hypermarkdown
go build -o hypermarkdown
sudo mv hypermarkdown /usr/local/bin/

Usage

hypermarkdown input.md output.html

That's it. Pass in a Markdown file, get back an HTML file. If either argument is missing, hypermarkdown prints:

Error: usage: hypermarkdown <input.md> <output.html>

Supported Markdown

Headings

# H1
## H2
### H3
#### H4
##### H5
###### H6

Setext H1
=========

Setext H2
---------

Text Formatting

**bold**
***bold italic***
*italic*
__underline__
~~strikethrough~~
`inline code`

Backslash Escapes

Prefix any ASCII punctuation character with \ to render it literally:

\*not italic\**not italic*
\**not bold\****not bold**
\[not a link\](url)   →  [not a link](url)
\\\

Only ASCII punctuation is escapable. \n and other non-punctuation sequences are left as-is.

Links and Images

Inline syntax:

[link text](https://example.com)
[link text](https://example.com "optional title")
![alt text](image.jpg)
![alt text](image.jpg "optional title")

Reference-style syntax:

[link text][ref-id]
![alt text][img-id]

[ref-id]: https://example.com
[img-id]: image.jpg

Reference IDs are case-insensitive and can be defined anywhere in the document. Link and image labels may wrap across lines inside a paragraph.

Lists

- unordered item
* also unordered
+ plus marker

1. ordered item
2. another item
1) ordered with parenthesis

Nested lists are supported — indent with spaces or tabs:

- fruit
  - apples
  - bananas
- vegetables
  1. carrots
  2. peas

Mixed ul/ol types are allowed at different nesting levels.

Task Lists

- [x] completed task
- [X] also completed
- [ ] incomplete task

Renders as disabled checkboxes inside list items.

Blockquotes

> This is a blockquote.
> It can span multiple lines.

Nested blockquotes are supported:

> Outer quote.
> > Inner quote.
> > > Triple nested.

Code Blocks

Fenced with ~~~ or triple backticks:

~~~
code here
~~~

~~~python
import os
~~~

```go
fmt.Println("hello")
```

Or indented with 4 spaces:

    # this is code, not a heading
    for i in range(10): ...

Tables

| Name    | Age | City      |
| :------ | :-: | --------: |
| Jason   | 25  | London    |
| Bob     | 30  | New York  |

Column alignment is controlled by colon placement on the separator row:

  • :--- — left
  • :---: — center
  • ---: — right
  • --- — no alignment attribute

Alignment is applied as an inline style="text-align:..." on each <th> and <td>. Inline formatting works inside cells. Use \| for a literal pipe inside a table cell.

HTML Character Escaping

&, <, >, ", and ' in plain text are automatically escaped:

1 < 2 and 3 > 2     →    1 &lt; 2 and 3 &gt; 2
Fish & chips         →    Fish &amp; chips
"quoted"             →    &quot;quoted&quot;

Raw inline HTML is escaped by design rather than passed through.

Footnotes

Here is a sentence with a footnote.[^1]
Another sentence with a named footnote.[^note]

[^1]: This is the footnote text.
[^note]: This is a named footnote.
    Continuation lines (indented 4 spaces or a tab) are appended to the definition.

Inline references render as superscript links: [1]
Definitions are collected in a pre-scan and rendered at the bottom of the document inside <section class="footnotes">.

HTML Output

Output files include a complete HTML shell:

  • <!DOCTYPE html>
  • <meta charset="utf-8">
  • responsive viewport meta tag
  • <title>hypermarkdown output</title>
  • small default CSS for readable documents, code blocks, tables, and blockquotes

Other

---        horizontal rule (also *** and ___)
| Line one  line block
| Line two

Project Structure

hypermarkdown/
├── main.go                          — reads args, calls parser, writes output
├── go.mod
└── internal/
    ├── parser/
    │   ├── parser.go                — block-based parser pipeline
    │   ├── block_helpers.go         — shared parser helpers
    │   ├── blockquotes.go           — recursive blockquote rendering
    │   ├── footnotes.go             — footnote collection and parsing
    │   ├── links.go                 — reference-style link/image resolution
    │   ├── lists.go                 — nested list rendering
    │   ├── tables.go                — pipe table rendering with alignment
    │   └── parser_test.go           — parser and block-level tests
    └── converters/
        ├── headers.go               — ATX and setext heading extraction
        ├── formatting.go            — inline formatting (bold, italic, etc.)
        ├── headers_test.go
        └── formatting_test.go

How It Works

The parser uses a block-based approach rather than line-by-line processing.

Consecutive lines of the same type are accumulated into a block, then flushed as a unit when a different block type is detected. This allows correct wrapping of <ul>, <ol>, <blockquote>, and <p> tags across multiple lines.

Block detection order (order matters for correctness):

  1. Code fences (~~~ or triple backticks) — highest priority
  2. Lines inside a fence — collected raw, no formatting applied
  3. Blank lines — flush all pending blocks
  4. Footnote definitions — collected in a pre-scan and skipped in body output
  5. Footnote continuation lines (indented, following a definition) — skipped
  6. Link definitions — collected in a pre-scan and skipped in body output
  7. Horizontal rules — before list/italic to avoid --- and * conflicts
  8. Indented code (4 spaces or tab) — before ATX headers so # foo is code
  9. ATX headers (#)
  10. Setext headers (look-ahead at next line)
  11. Blockquotes (>) — rendered recursively to support nesting
  12. Unordered lists (- / * / +)
  13. Ordered lists (1. / 1))
  14. Pipe tables
  15. Line blocks (|)
  16. Paragraph text — default fallback

Inline formatting runs after block detection, on the text content only. The pipeline is:

  1. Escaped backticks are protected first so ``` suppresses code spans
  2. Code spans are protected, so their contents stay literal
  3. Other backslash escapes are protected
  4. Images, bold+italic, bold, italic, underline, strikethrough, links, and autolinks are applied in order
  5. Backslash escapes, code spans, and escaped backticks are restored

This ensures that markdown syntax inside backticks is never processed, and that \* suppresses italic rather than producing <em>.


Running Tests

go test ./...

136 tests covering CLI behavior, block types, inline formatting, edge cases, and ordering rules.


Known Limitations

  • Math ($...$) — not standard markdown, not supported
  • Dashless pipe tables — only standard pipe tables with a separator row are supported
  • Raw HTML pass-through — raw HTML is escaped for safety rather than rendered as HTML

Built With

  • Go standard library only
  • strings, fmt, regexp, os
  • No third-party dependencies

About

A fast, zero-dependency Markdown-to-HTML CLI tool written in Go. Supports headings, bold, italic, lists, nested lists, tables, code blocks, blockquotes, links, images, footnotes, and HTML escaping. Built on the standard library only (no external packages required).

Topics

Resources

Stars

Watchers

Forks

Used by

Contributors

Languages