@complexthings/md-to-adf
v1.0.0
Published
Translate Markdown (Github) into Atlassian Document Format (ADF)
Maintainers
Readme
@complexthings/md-to-adf
Convert GitHub-flavored Markdown to Atlassian Document Format (ADF)
ADF is the rich-text format used by Atlassian products (Jira, Confluence) to represent formatted content. This package lets you take any GitHub-flavored Markdown string and get back a fully structured ADF Document object, ready to send to the Atlassian REST API.
Installation
npm install @complexthings/md-to-adfUsage
Basic Usage
import translateGITHUBMarkdownToADF from '@complexthings/md-to-adf'
const markdown = `# My Heading
This is a paragraph with **bold** and *italic* text.
- First item
- Second item
- Nested item
`
const adfDocument = translateGITHUBMarkdownToADF(markdown)
// adfDocument is an adf-builder Document object.
// Call .toJSON() to get the plain JSON representation.
console.log(JSON.stringify(adfDocument, null, 2))The returned object serializes to standard ADF JSON:
{
"version": 1,
"type": "doc",
"content": [
{
"type": "heading",
"attrs": { "level": 1 },
"content": [{ "type": "text", "text": "My Heading" }]
},
{
"type": "paragraph",
"content": [
{ "type": "text", "text": "This is a paragraph with " },
{ "type": "text", "text": "bold", "marks": [{ "type": "strong" }] },
{ "type": "text", "text": " and " },
{ "type": "text", "text": "italic", "marks": [{ "type": "em" }] },
{ "type": "text", "text": " text." }
]
},
{
"type": "bulletList",
"content": [
{
"type": "listItem",
"content": [
{ "type": "paragraph", "content": [{ "type": "text", "text": "First item" }] }
]
}
]
}
]
}API
translateGITHUBMarkdownToADF(markdownText)
The single public export of this package. Takes a Markdown string and returns an ADF Document object (from the adf-builder library).
| Parameter | Type | Description |
| -------------- | -------- | ------------------------------------ |
| markdownText | string | GitHub-flavored Markdown source text |
Returns: Document — an adf-builder Document instance. Pass it directly to the Atlassian REST API or call .toJSON() / JSON.stringify() to obtain plain JSON.
import translateGITHUBMarkdownToADF from '@complexthings/md-to-adf'
const adf = translateGITHUBMarkdownToADF('# Hello\n\nWorld')
// Send to Confluence Page API, Jira issue body, etc.Internal Modules
The following functions are the building blocks used by translateGITHUBMarkdownToADF. They are exported from their respective source files but are implementation details — prefer using the top-level function unless you need to work with the intermediate representation directly.
buildTreeFromMarkdown(rawTextMarkdown) — source/markdownHandling.js
Parses a raw Markdown string into an intermediate representation (IR) tree (IRTreeNode[]). Handles code block collapsing, blockquote merging, paragraph continuation, and list nesting.
parseMarkdownLinetoIR(markdownLineTextWithTabs) — source/markdownParsing.js
Parses a single Markdown line into an IRElement object with adfType, textToEmphasis, typeParam, textPosition, and optionally nodeAttached. Used internally by buildTreeFromMarkdown.
fillADFNodesWithMarkdown(currentParentNode, currentArrayOfNodesOfSameIndent) — source/adfHandling.js
Recursively walks an IR tree and attaches ADF nodes to a parent Document. Handles list merging, list items, blockquotes, code blocks, paragraphs, and inline content.
attachTextToNodeSliceEmphasis(parentNode, textToEmphasis) — source/adfEmphasisParsing.js
Parses a text string character-by-character for emphasis markers (*, _, ~~) and attaches ADF Text nodes with the appropriate marks (em, strong, strike) to the given parent node.
Supported Markdown Features
The following elements are recognized and converted to their ADF equivalents:
| Markdown | ADF Type | Notes |
| --------------------------------- | ---------------- | --------------------------------------------------- |
| # … ###### | heading | All six heading levels (h1–h6) |
| Plain text / continuation lines | paragraph | Consecutive non-blank lines collapse into one block |
| * text, - text, + text | bulletList | Nested via indentation (2-space increments) |
| 1. text, 1) text | orderedList | Nested; preserves starting number |
| ``` … ``` | codeBlock | Fenced; optional language tag (```js) |
| `code` | inline code | text node with code mark |
| *italic* / _italic_ | italic | text node with em mark |
| **bold** / __bold__ | bold | text node with strong mark |
| ~~strikethrough~~ | strikethrough | text node with strike mark |
| [title](url) / [title](url "title") | link | text node with link mark |
| :shortname: | emoji | emoji node with shortName attribute |
| > text | blockQuote | Consecutive > lines merge into a single block |
| ---, ***, ___ (3+ chars) | rule (divider) | Horizontal rule |
Emphasis markers can be combined: ***bold italic*** produces a text node with both strong and em marks. Strikethrough can be layered with bold/italic.
Code blocks inside list items are supported — the code block becomes a child of the list item's paragraph.
Migration from md-to-adf
If you are coming from the original md-to-adf package (unscoped, CommonJS):
| Topic | md-to-adf (original) | @complexthings/md-to-adf (this package) |
| --------------- | ----------------------------------- | ----------------------------------------- |
| Package name | md-to-adf | @complexthings/md-to-adf |
| Module system | CommonJS (require) | ESM only (import) |
| Node.js version | unconstrained | >= 22.0.0 |
| Function name | translateGITHUBMarkdownToADF | translateGITHUBMarkdownToADF (same) |
| Import syntax | const fn = require('md-to-adf') | import fn from '@complexthings/md-to-adf' |
The function signature and return value are identical. Only the import statement needs to change.
Node.js Version
Requires Node.js 22 or later ("engines": { "node": ">=22.0.0" }).
Known Limitations
- No image rendering. Markdown images (
) are parsed as links — the alt text becomes a linkedtextnode rather than animageADF node. - No table support. Markdown tables (
| col | col |) are not recognized and will be treated as plain paragraph text. - No task list support. GitHub checkboxes (
- [ ] item) are not handled — they fall through to plain bullet list items. - No HTML pass-through. Inline or block HTML is treated as literal text.
- No reference-style links. Only inline links (
[text](url)) are supported; reference-style links ([text][ref]) are not. - No setext-style headings. Only ATX headings (
# H1) are recognized; underline-style headings (H1\n===) are not. - Strikethrough requires
~~delimiters. Single-tilde (~text~) is not recognized.
Development
npm test # Run tests (Vitest)
npm run test:watch # Run tests in watch mode
npm run test:coverage # Run tests with coverage report
npm run lint # Lint source files
npm run lint:fix # Lint and auto-fix
npm run build # Build to dist/Tests are written with Vitest. The original test suite used BDD-style Gherkin feature files (test/feature/*.feature) with step definitions (test/step/*.steps.js) via jest-cucumber-fusion. Fixture inputs and expected ADF outputs live in test/markdown-capture/.
Contributing
- Fork the repository and create a feature branch.
- Write a failing test that describes the behavior you're adding or fixing.
- Implement the change and make the test pass.
- Run
npm testandnpm run lint— both must be clean. - Open a pull request with a clear description of what changed and why.
Please keep pull requests focused: one feature or fix per PR.
License
Apache-2.0
