FBMarkdown
FBMarkdown is an extensible parser and renderer for GitHub Flavored Markdown,
written in Hack., (*1)
It is used to render the Hack and HHVM documentation., (*2)
Why another Markdown library?
For docs.hhvm.com, we wanted:, (*3)
- GitHub Flavored Markdown for familiarity
- support for custom extensions
Originally, the Ruby GFM pipeline was the best fit; over time, we started to want to:, (*4)
- make it easier and faster to contribute to docs.hhvm.com
- remove the Ruby dependencies to make it easy to render markdown in other Hack projects
- produce and mutate an AST before rendering
- support multiple renders
FBMarkdown exists to address all of these goals., (*5)
Requirements
Installing FBMarkdown
hhvm composer.phar require facebook/fbmarkdown
Using 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., (*6)
-
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., (*7)
-
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., (*8)
-
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., (*9)
To make changes to these default settings:
- You may alter the keyset of allowed URI schemes by calling the Parser function setAllowedURISchemes()
.
- You may enable embedded HTML by calling the Parser function enableHTML_UNSAFE()
. N.B.: For complete compatibility with GitHub Flavored Markdown, support for embedded HTML must be enabled.
- You may disable image filtering by calling the Renderer function disableImageFiltering()
.
- You may add rel="nofollow ugc"
to all links by calling the Renderer function addNoFollowUGCAllLinks()
., (*10)
If you are re-using contexts to render multiple independent snippets, you will need to call ->resetFileData()
on the context., (*11)
How FBMarkdown works
Parsing
- The classes in the
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.
- The contents of the blocks ('inlines') are parsed using the classes in the
Facebook\Markdown\Inlines
namespace.
- Finally, the classes of the
Facebook\Markdown\Blocks
namespace are used to
represent the fully parsed AST - blocks and Inlines.
Rendering
The AST is recursively walked, emitting output for each note - e.g. the HTML renderer produces strings., (*12)
Extending FBMarkdown
There are 2 main ways to extend FBMarkdown: extending the parser, and transforming the AST., (*13)
Extending The Parser
Inlines
Extend Facebook\Markdown\Inlines\Inline
or a subclass, and pass your classname to
$render_ctx->getInlineContext()->prependInlineTypes(...)
., (*14)
There are then several approaches to rendering:
- instantiate your subclass, and add support for it to a custom renderer
- instantiate your subclass, and make it implement the Facebook\Markdown\RenderableAsHTML
interface
- if it could be replaced with several existing inlines, return a
Facebook\Markdown\Inlines\InlineSequence
, then you won't need to extend the renderer., (*15)
Blocks
You will need to implement the Facebook\Markdown\UnparsedBlocks\BlockProducer
interface, and pass your classname
to $render_ctx->getBlockContext()->prependBlockTypes(...)
., (*16)
There are then several approaches to rendering:
- create a subclass of Block
, and add support for it to a custom renderer
- create a subclass of Block
, and make it implement the Facebook\Markdown\RenderableAsHTML
interface
- if it could be replaced with several existing blocks, return a
Facebook\Markdown\Blocks\BlockSequence
- if it could be replaced with a paragraph of inlines, return a Facebook\Markdown\Blocks\InlineSequenceBlock
, (*17)
Extend Facebook\Markdown\RenderFilter
, and pass it to $render_ctx->appendFilters(...)
., (*18)
Examples
The Hack and HHVM documentation uses most of these approaches; see:, (*19)
License
FBMarkdown is MIT-licensed., (*20)
FBMarkdown may contain third-party software; see third_party_notices.txt for details., (*21)