FBMarkdown is an extensible parser and renderer for GitHub Flavored Markdown, written in Hack.
It is used to render the Hack and HHVM documentation.
For docs.hhvm.com, we wanted:
Originally, the Ruby GFM pipeline was the best fit; over time, we started to want to:
FBMarkdown exists to address all of these goals.
hhvm composer.phar require facebook/fbmarkdown
use namespace Facebook\Markdown;
function render(string $markdown): string {
$ast = Markdown\parse(new Markdown\ParserContext(), $markdown);
$html = (new Markdown\HTMLRenderer(
new Markdown\RenderContext()
))->render($ast);
return $html;
}
FBMarkdown currently supports three types of Markdown sources, with plans to expand: trusted, sponsored, and user-generated content.
Trusted content mode: Embedded HTML is enabled, and all URI schemes are enabled and will be parsed as links. In addition, all images are processed normally.
Sponsored mode: HTML rendering is enabled, but limited to allowed tags only (defined in TagFilterExtension
, based on the GFM spec). Additionally, URIs are limited to the {http
, https
, irc
, and mailto
} schemes, and rel="nofollow ugc"
is added to all links.
User-generated content: All HTML is disabled, as are links and images regardless of schemes. If links are re-enabled, rel="nofollow ugc"
will be added to all links.
To make changes to these default settings:
setAllowedURISchemes()
.enableHTML_UNSAFE()
. N.B.: For complete compatibility with GitHub Flavored Markdown, support for embedded HTML must be enabled. disableImageFiltering()
.rel="nofollow ugc"
to all links by calling the Renderer function addNoFollowUGCAllLinks()
.If you are re-using contexts to render multiple independent snippets, you will need to call ->resetFileData()
on the context.
Facebook\Markdown\UnparsedBlocks
namespace convert
markdown text to a tree of nodes representing the block structure of
the document, however the content of the blocks is unparsed.Facebook\Markdown\Inlines
namespace.Facebook\Markdown\Blocks
namespace are used to
represent the fully parsed AST - blocks and Inlines.The AST is recursively walked, emitting output for each note - e.g. the HTML renderer produces strings.
There are 2 main ways to extend FBMarkdown: extending the parser, and transforming the AST.
Extend Facebook\Markdown\Inlines\Inline
or a subclass, and pass your classname to
$render_ctx->getInlineContext()->prependInlineTypes(...)
.
There are then several approaches to rendering:
Facebook\Markdown\RenderableAsHTML
interfaceFacebook\Markdown\Inlines\InlineSequence
, then you won't need to extend the renderer.You will need to implement the Facebook\Markdown\UnparsedBlocks\BlockProducer
interface, and pass your classname
to $render_ctx->getBlockContext()->prependBlockTypes(...)
.
There are then several approaches to rendering:
Block
, and add support for it to a custom rendererBlock
, and make it implement the Facebook\Markdown\RenderableAsHTML
interfaceFacebook\Markdown\Blocks\BlockSequence
Facebook\Markdown\Blocks\InlineSequenceBlock
Extend Facebook\Markdown\RenderFilter
, and pass it to $render_ctx->appendFilters(...)
.
The Hack and HHVM documentation uses most of these approaches; see:
FBMarkdown is MIT-licensed.
FBMarkdown may contain third-party software; see third_party_notices.txt for details.