1- import { Renderer , Slugger } from 'marked' ;
1+ import { Renderer , Tokens } from 'marked' ;
22import { basename , extname } from 'path' ;
3+ import slugify from 'slugify' ;
4+ import { highlightCodeBlock } from '../highlight-files/highlight-code-block' ;
35
46/** Regular expression that matches example comments. */
57const exampleCommentRegex = / < ! - - \s * e x a m p l e \( \s * ( [ ^ ) ] + ) \) \s * - - > / g;
@@ -18,46 +20,53 @@ export class DocsMarkdownRenderer extends Renderer {
1820 /** Set of fragment links discovered in the currently rendered file. */
1921 private _referencedFragments = new Set < string > ( ) ;
2022
21- /**
22- * Slugger provided by the `marked` package. Can be used to create unique
23- * ids for headings.
24- */
25- private _slugger = new Slugger ( ) ;
23+ /** IDs that have been generated during Markdown parsing. */
24+ private _seenIds = new Set < string > ( ) ;
2625
2726 /**
2827 * Transforms a markdown heading into the corresponding HTML output. In our case, we
2928 * want to create a header-link for each H2, H3, and H4 heading. This allows users to jump to
3029 * specific parts of the docs.
3130 */
32- heading ( label : string , level : number , raw : string ) {
33- if ( level === 2 || level === 3 || level === 4 || level === 5 || level === 6 ) {
34- const headingId = this . _slugger . slug ( raw ) ;
31+ heading ( tag : Tokens . Heading ) {
32+ const depth = tag . depth ;
33+ const content = this . parser . parseInline ( tag . tokens ) ;
34+
35+ if ( depth === 2 || depth === 3 || depth === 4 || depth === 5 || depth === 6 ) {
36+ const headingId = slugify ( tag . text , { lower : true , strict : true } ) ;
37+
38+ this . _seenIds . add ( headingId ) ;
3539 return `
36- <h${ level } id="${ headingId } " class="docs-header-link">
40+ <h${ depth } id="${ headingId } " class="docs-header-link">
3741 <span header-link="${ headingId } "></span>
38- ${ label }
39- </h${ level } >
42+ ${ content }
43+ </h${ depth } >
4044 ` ;
4145 }
4246
43- return `<h${ level } >${ label } </h${ level } >` ;
47+ return `<h${ depth } >${ content } </h${ depth } >` ;
4448 }
4549
4650 /** Transforms markdown links into the corresponding HTML output. */
47- link ( href : string , title : string , text : string ) {
51+ link ( link : Tokens . Link ) {
52+ const { href} = link ;
53+
4854 // We only want to fix up markdown links that are relative and do not refer to guides already.
4955 // Otherwise we always map the link to the "guide/" path.
5056 // TODO(devversion): remove this logic and just disallow relative paths.
5157 if ( ! href . startsWith ( 'http' ) && ! href . startsWith ( '#' ) && ! href . includes ( 'guide/' ) ) {
52- return super . link ( `guide/${ basename ( href , extname ( href ) ) } ` , title , text ) ;
58+ return super . link ( {
59+ ...link ,
60+ href : `guide/${ basename ( href , extname ( href ) ) } ` ,
61+ } ) ;
5362 }
5463
5564 // Keep track of all fragments discovered in a file.
5665 if ( href . startsWith ( '#' ) ) {
5766 this . _referencedFragments . add ( href . slice ( 1 ) ) ;
5867 }
5968
60- return super . link ( href , title , text ) ;
69+ return super . link ( link ) ;
6170 }
6271
6372 /**
@@ -82,8 +91,8 @@ export class DocsMarkdownRenderer extends Renderer {
8291 * turns into
8392 * `<div material-docs-example="name"></div>`
8493 */
85- html ( html : string ) {
86- html = html . replace ( exampleCommentRegex , ( _match : string , content : string ) => {
94+ html ( content : Tokens . HTML | Tokens . Tag ) {
95+ return content . raw . replace ( exampleCommentRegex , ( _match : string , content : string ) => {
8796 let replacement : string ;
8897
8998 // using [\s\S]* because .* does not match line breaks
@@ -102,8 +111,11 @@ export class DocsMarkdownRenderer extends Renderer {
102111
103112 return `${ exampleStartMarker } ${ replacement } ${ exampleEndMarker } ` ;
104113 } ) ;
114+ }
105115
106- return super . html ( html ) ;
116+ code ( block : Tokens . Code ) : string {
117+ const langClass = block . lang ? ` class="language-${ block . lang } "` : '' ;
118+ return `<pre><code${ langClass } >${ highlightCodeBlock ( block . text , block . lang ) } </code></pre>` ;
107119 }
108120
109121 /**
@@ -116,7 +128,7 @@ export class DocsMarkdownRenderer extends Renderer {
116128 // Collect any fragment links that do not resolve to existing fragments in the
117129 // rendered file. We want to error for broken fragment links.
118130 this . _referencedFragments . forEach ( id => {
119- if ( this . _slugger . seen [ id ] === undefined ) {
131+ if ( ! this . _seenIds . has ( id ) ) {
120132 failures . push ( `Found link to "${ id } ". This heading does not exist.` ) ;
121133 }
122134 } ) ;
@@ -127,7 +139,7 @@ export class DocsMarkdownRenderer extends Renderer {
127139 process . exit ( 1 ) ;
128140 }
129141
130- this . _slugger . seen = { } ;
142+ this . _seenIds . clear ( ) ;
131143 this . _referencedFragments . clear ( ) ;
132144
133145 const markdownOpen = '<div class="docs-markdown">' ;
0 commit comments