33const marked = require ( 'marked' ) ;
44const highlightjs = require ( 'highlightjs' ) ;
55
6- module . exports = function compileMarkdown ( string , options ) {
7- let config = { highlight } ;
6+ module . exports = function compileMarkdown ( source , config ) {
7+ let tokens = marked . lexer ( source ) ;
8+ let markedOptions = {
9+ highlight,
10+ renderer : new HBSRenderer ( config )
11+ } ;
812
9- if ( options && options . targetHandlebars ) {
10- config . renderer = new HBSRenderer ( ) ;
13+ if ( config && config . targetHandlebars ) {
14+ tokens = compactParagraphs ( tokens ) ;
1115 }
1216
13- return `<div class="docs-md">${ marked ( string , config ) } </div>` ;
17+ return `<div class="docs-md">${ marked . parser ( tokens , markedOptions ) . trim ( ) } </div>` ;
1418} ;
1519
1620function highlight ( code , lang ) {
@@ -21,22 +25,73 @@ function highlight(code, lang) {
2125 }
2226}
2327
28+ // Whitespace can imply paragraphs in Markdown, which can result
29+ // in interleaving between <p> tags and block component invocations,
30+ // so this scans the Marked tokens to turn things like this:
31+ // <p>{{#my-component}}<p>
32+ // <p>{{/my-component}}</p>
33+ // Into this:
34+ // <p>{{#my-component}} {{/my-component}}</p>
35+ function compactParagraphs ( tokens ) {
36+ let compacted = [ ] ;
37+
38+ compacted . links = tokens . links ;
39+
40+ let balance = 0 ;
41+ for ( let token of tokens ) {
42+ if ( balance === 0 ) {
43+ compacted . push ( token ) ;
44+ } else {
45+ let last = compacted [ compacted . length - 1 ] ;
46+ last . text = `${ last . text } ${ token . text } ` ;
47+ }
48+
49+ balance += count ( / \{ \{ # / g, token . text ) ;
50+ balance -= count ( / \{ \{ \/ / g, token . text ) ;
51+ }
52+
53+ return compacted ;
54+ }
55+
56+ function count ( regex , string ) {
57+ let total = 0 ;
58+ while ( regex . exec ( string ) ) total ++ ;
59+ return total ;
60+ }
61+
2462class HBSRenderer extends marked . Renderer {
25- // Escape curlies in code spans/blocks to avoid treating them as Handlebars
63+ constructor ( config ) {
64+ super ( ) ;
65+ this . config = config || { } ;
66+ }
67+
2668 codespan ( ) {
27- return this . _escapeCurlies ( super . codespan . apply ( this , arguments ) ) ;
69+ return this . _processCode ( super . codespan . apply ( this , arguments ) ) ;
2870 }
2971
3072 code ( ) {
31- let code = this . _escapeCurlies ( super . code . apply ( this , arguments ) ) ;
73+ let code = this . _processCode ( super . code . apply ( this , arguments ) ) ;
3274 return code . replace ( / ^ < p r e > / , '<pre class="docs-md__code">' ) ;
3375 }
3476
3577 // Unescape quotes in text, as they may be part of a Handlebars statement
3678 text ( ) {
37- return super . text . apply ( this , arguments )
38- . replace ( / & q u o t ; | & # 3 4 ; / g, `"` )
39- . replace ( / & a p o s ; | & # 3 9 ; / g, `'` ) ;
79+ let text = super . text . apply ( this , arguments ) ;
80+ if ( this . config . targetHandlebars ) {
81+ text = text
82+ . replace ( / & q u o t ; | & # 3 4 ; / g, `"` )
83+ . replace ( / & a p o s ; | & # 3 9 ; / g, `'` ) ;
84+ }
85+ return text ;
86+ }
87+
88+ // Escape curlies in code spans/blocks to avoid treating them as Handlebars
89+ _processCode ( string ) {
90+ if ( this . config . targetHandlebars ) {
91+ string = this . _escapeCurlies ( string ) ;
92+ }
93+
94+ return string ;
4095 }
4196
4297 _escapeCurlies ( string ) {
0 commit comments