1
- import { Renderer , Slugger } from 'marked' ;
1
+ import { Renderer , Tokens } from 'marked' ;
2
2
import { basename , extname } from 'path' ;
3
+ import slugify from 'slugify' ;
4
+ import { highlightCodeBlock } from '../highlight-files/highlight-code-block' ;
3
5
4
6
/** Regular expression that matches example comments. */
5
7
const exampleCommentRegex = / < ! - - \s * e x a m p l e \( \s * ( [ ^ ) ] + ) \) \s * - - > / g;
@@ -18,46 +20,53 @@ export class DocsMarkdownRenderer extends Renderer {
18
20
/** Set of fragment links discovered in the currently rendered file. */
19
21
private _referencedFragments = new Set < string > ( ) ;
20
22
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 > ( ) ;
26
25
27
26
/**
28
27
* Transforms a markdown heading into the corresponding HTML output. In our case, we
29
28
* want to create a header-link for each H2, H3, and H4 heading. This allows users to jump to
30
29
* specific parts of the docs.
31
30
*/
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 ) ;
35
39
return `
36
- <h${ level } id="${ headingId } " class="docs-header-link">
40
+ <h${ depth } id="${ headingId } " class="docs-header-link">
37
41
<span header-link="${ headingId } "></span>
38
- ${ label }
39
- </h${ level } >
42
+ ${ content }
43
+ </h${ depth } >
40
44
` ;
41
45
}
42
46
43
- return `<h${ level } >${ label } </h${ level } >` ;
47
+ return `<h${ depth } >${ content } </h${ depth } >` ;
44
48
}
45
49
46
50
/** 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
+
48
54
// We only want to fix up markdown links that are relative and do not refer to guides already.
49
55
// Otherwise we always map the link to the "guide/" path.
50
56
// TODO(devversion): remove this logic and just disallow relative paths.
51
57
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
+ } ) ;
53
62
}
54
63
55
64
// Keep track of all fragments discovered in a file.
56
65
if ( href . startsWith ( '#' ) ) {
57
66
this . _referencedFragments . add ( href . slice ( 1 ) ) ;
58
67
}
59
68
60
- return super . link ( href , title , text ) ;
69
+ return super . link ( link ) ;
61
70
}
62
71
63
72
/**
@@ -82,8 +91,8 @@ export class DocsMarkdownRenderer extends Renderer {
82
91
* turns into
83
92
* `<div material-docs-example="name"></div>`
84
93
*/
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 ) => {
87
96
let replacement : string ;
88
97
89
98
// using [\s\S]* because .* does not match line breaks
@@ -102,8 +111,11 @@ export class DocsMarkdownRenderer extends Renderer {
102
111
103
112
return `${ exampleStartMarker } ${ replacement } ${ exampleEndMarker } ` ;
104
113
} ) ;
114
+ }
105
115
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>` ;
107
119
}
108
120
109
121
/**
@@ -116,7 +128,7 @@ export class DocsMarkdownRenderer extends Renderer {
116
128
// Collect any fragment links that do not resolve to existing fragments in the
117
129
// rendered file. We want to error for broken fragment links.
118
130
this . _referencedFragments . forEach ( id => {
119
- if ( this . _slugger . seen [ id ] === undefined ) {
131
+ if ( ! this . _seenIds . has ( id ) ) {
120
132
failures . push ( `Found link to "${ id } ". This heading does not exist.` ) ;
121
133
}
122
134
} ) ;
@@ -127,7 +139,7 @@ export class DocsMarkdownRenderer extends Renderer {
127
139
process . exit ( 1 ) ;
128
140
}
129
141
130
- this . _slugger . seen = { } ;
142
+ this . _seenIds . clear ( ) ;
131
143
this . _referencedFragments . clear ( ) ;
132
144
133
145
const markdownOpen = '<div class="docs-markdown">' ;
0 commit comments