facebook / fbmarkdown
Markdown parser and renderer for Hack
Installs: 20 677
Dependents: 3
Suggesters: 0
Security: 0
Stars: 29
Watchers: 19
Forks: 10
Open Issues: 8
Language:Hack
Requires
- hhvm: ^4.128
- hhvm/type-assert: ^3.1|^4.0
Requires (Dev)
- facebook/fbexpect: ^2.1.0
- hhvm/hacktest: ^2.0
- hhvm/hhast: ^4.0
- hhvm/hhvm-autoload: ^2.0|^3.0
README
FBMarkdown is an extensible parser and renderer for GitHub Flavored Markdown, written in Hack.
It is used to render the Hack and HHVM documentation.
Why another Markdown library?
For docs.hhvm.com, we wanted:
- 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:
- 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.
Requirements
- HHVM 3.24 or above.
- hhvm-autoload
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.
-
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
, andmailto
} schemes, andrel="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:
- 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 functionaddNoFollowUGCAllLinks()
.
If you are re-using contexts to render multiple independent snippets, you will need to call ->resetFileData()
on the context.
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.
Extending FBMarkdown
There are 2 main ways to extend FBMarkdown: extending the parser, and transforming the AST.
Extending The Parser
Inlines
Extend Facebook\Markdown\Inlines\Inline
or a subclass, and pass your classname to
$render_ctx->getInlineContext()->prependInlineTypes(...)
.
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.
Blocks
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:
- create a subclass of
Block
, and add support for it to a custom renderer - create a subclass of
Block
, and make it implement theFacebook\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
Transforming The AST
Extend Facebook\Markdown\RenderFilter
, and pass it to $render_ctx->appendFilters(...)
.
Examples
The Hack and HHVM documentation uses most of these approaches; see:
License
FBMarkdown is MIT-licensed.
FBMarkdown may contain third-party software; see third_party_notices.txt for details.