Skip to content

pkalusek/markdown-to-adf

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

markdown-to-adf

Note: This library is under active development and not yet stable. API may change.

A PHP library that converts between GitHub Flavored Markdown (GFM) and Atlassian Document Format (ADF) for use with Jira and Confluence APIs. Supports both directions: Markdown → ADF and ADF → Markdown.

Inspired by marklassian (JavaScript).

Installation

composer require kalusek/markdown-to-adf

Quick start

Markdown → ADF

use Kalusek\MarkdownToAdf\MarkdownToAdf;

$adf = MarkdownToAdf::convert('# Hello **World**');

echo json_encode($adf, JSON_PRETTY_PRINT);

Output:

{
    "version": 1,
    "type": "doc",
    "content": [
        {
            "type": "heading",
            "attrs": { "level": 1 },
            "content": [
                { "type": "text", "text": "Hello " },
                { "type": "text", "text": "World", "marks": [{ "type": "strong" }] }
            ]
        }
    ]
}

ADF → Markdown

use Kalusek\MarkdownToAdf\AdfToMarkdown;

$adf = [
    'version' => 1,
    'type' => 'doc',
    'content' => [
        ['type' => 'heading', 'attrs' => ['level' => 2], 'content' => [['type' => 'text', 'text' => 'Problem']]],
        ['type' => 'paragraph', 'content' => [
            ['type' => 'text', 'text' => 'The table is '],
            ['type' => 'text', 'text' => 'too wide', 'marks' => [['type' => 'strong']]],
            ['type' => 'text', 'text' => '.'],
        ]],
    ],
];

echo AdfToMarkdown::convert($adf);

Output:

## Problem

The table is **too wide**.

Supported Markdown features

  • Headings (H1-H6)
  • Paragraphs and line breaks (hard + soft)
  • Bold, italic, strikethrough, inline code
  • Links and images (images become block-level mediaSingle nodes)
  • Fenced and indented code blocks with language
  • Ordered and unordered lists with nesting
  • Blockquotes
  • Horizontal rules
  • Tables
  • GFM task lists (- [ ] / - [x]) with nesting
  • Raw ADF embedding via <adf> tags

Supported ADF node types (ADF → Markdown)

  • Headings, paragraphs, hard breaks
  • Bold, italic, strikethrough, inline code, links, underline
  • Bullet lists, ordered lists, nested lists
  • Task lists (TODO / DONE)
  • Fenced code blocks with language
  • Blockquotes and panels
  • Horizontal rules
  • Tables (with header rows)
  • Images (mediaSingle / media)
  • Mentions, emoji, inline cards

Instance API

For more control, instantiate the class:

$converter = new MarkdownToAdf(debug: true, bail: false);
$adf = $converter->parse($markdown);

Options

Option Type Default Description
debug bool false Emit E_USER_WARNING when an unrecognized node type is encountered
bail bool false Throw RuntimeException when an unrecognized node type is encountered

Custom node handlers

Register handlers for CommonMark node types you want to convert differently, or for custom extension nodes:

use League\CommonMark\Node\Node;

$converter = new MarkdownToAdf();

// Override how headings are converted
$converter->addHandler(Heading::class, function (Node $node) {
    return [[
        'type' => 'heading',
        'attrs' => ['level' => min($node->getLevel(), 3)], // cap at H3
        'content' => [['type' => 'text', 'text' => 'Custom: ' . $node->getLevel()]],
    ]];
});

$adf = $converter->parse($markdown);

Handlers receive a Node and must return list<array> (an array of ADF node arrays).

The processChildren() and processInlines() methods are public so custom handlers can recurse into child content:

$converter->addHandler(MyBlock::class, function (Node $node) use ($converter) {
    return [[
        'type' => 'panel',
        'attrs' => ['panelType' => 'info'],
        'content' => $converter->processChildren($node),
    ]];
});

Raw ADF embedding

Embed custom ADF nodes directly in Markdown using <adf> tags:

# My page

<adf>
{"type":"extension","attrs":{"extensionType":"com.atlassian.confluence.macro.core","extensionKey":"status","parameters":{"macroParams":{"title":{"value":"Done"},"colour":{"value":"Green"}}}}}
</adf>

More content after the macro.

Content must be valid JSON: either a single object or an array of objects, each with a "type" property.

Behavior notes

  • Empty input returns a valid empty ADF document: {"version": 1, "type": "doc", "content": []}
  • Mixed task lists (regular items + task items in the same list) are treated as regular bullet lists, since ADF taskList nodes don't support non-task children
  • Inline code marks only combine with link marks per the ADF spec. Other marks (bold, italic, strikethrough) are stripped when inside inline code.

Requirements

  • PHP 8.1+
  • league/commonmark ^2.6

License

MIT

About

A php package to convert markdown to adf

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages