1
1
import path from "path"
2
2
import spawn from "cross-spawn"
3
+ import type Md from "markdown-it"
4
+
5
+ type TreeItem = {
6
+ children : TreeItem [ ]
7
+ }
8
+ type TreeStack = { item : TreeItem ; level : number ; upper : TreeStack | null }
3
9
4
10
class TOCRenderer {
5
- constructor ( name ) {
6
- const item = { children : [ ] }
11
+ private readonly tree : TreeItem
12
+
13
+ private stack : TreeStack
14
+
15
+ public constructor ( ) {
16
+ const item : TreeItem = { children : [ ] }
7
17
this . tree = item
8
18
this . stack = { item, level : 1 , upper : null }
9
- this . name = name
10
19
}
11
20
12
- addMenu ( level , id , title ) {
21
+ public addMenu ( level : number , id : string , title : string ) {
13
22
if ( this . stack . level < level ) {
14
23
const parent = this . stack . item
15
24
const item = parent . children [ parent . children . length - 1 ]
@@ -24,53 +33,52 @@ class TOCRenderer {
24
33
this . stack . item . children . push ( item )
25
34
}
26
35
27
- toc ( ) {
36
+ public toc ( ) {
28
37
return this . tree
29
38
}
30
39
}
31
40
/**
32
41
* @param {import('markdown-it') } md
33
42
*/
34
- export default ( md ) => {
43
+ export default ( md : Md ) : void => {
35
44
md . core . ruler . push ( "custom_markdown" , ( state ) => {
36
45
const tokens = state . tokens
37
- tokens . unshift ( new state . Token ( "custom_markdown_data" ) )
46
+ tokens . unshift ( new state . Token ( "custom_markdown_data" , "" , 0 ) )
38
47
} )
39
- // eslint-disable-next-line camelcase -- ignore
48
+
40
49
md . renderer . rules . custom_markdown_data = (
41
50
tokens ,
42
51
_idx ,
43
52
_options ,
44
53
env ,
45
54
_self ,
46
55
) => {
47
- const name = path . basename ( env . id ) . replace ( / \. m d $ / , "" )
48
- const renderer = new TOCRenderer ( name )
56
+ const renderer = new TOCRenderer ( )
49
57
for ( let idx = 0 ; idx < tokens . length ; idx ++ ) {
50
58
const token = tokens [ idx ]
51
59
52
60
if ( token . type !== "heading_open" ) {
53
61
continue
54
62
}
55
- let level = Number ( token . tag . substr ( 1 ) )
63
+ const level = Number ( token . tag . slice ( 1 ) )
56
64
if ( level > 3 ) {
57
65
continue
58
66
}
59
67
// Aggregate the next token children text.
60
- const title = tokens [ idx + 1 ] . children
61
- . filter (
68
+ const title = tokens [ idx + 1 ]
69
+ . children ! . filter (
62
70
( token ) =>
63
71
token . type === "text" ||
64
72
token . type === "emoji" ||
65
73
token . type === "code_inline" ,
66
74
)
67
75
. reduce ( ( acc , t ) => acc + t . content , "" )
68
76
69
- let slug = token . attrGet ( "id" )
77
+ const slug = token . attrGet ( "id" ) !
70
78
renderer . addMenu ( level , slug , title )
71
79
}
72
80
73
- const fileInfo = { }
81
+ const fileInfo : { timestamp ?: number ; lastUpdated ?: string } = { }
74
82
const timestamp = getGitLastUpdatedTimestamp ( env . id )
75
83
if ( timestamp ) {
76
84
fileInfo . timestamp = timestamp
@@ -85,7 +93,7 @@ export default (md) => {
85
93
}
86
94
87
95
/** Get last updated timestamp */
88
- function getGitLastUpdatedTimestamp ( filePath ) {
96
+ function getGitLastUpdatedTimestamp ( filePath : string ) {
89
97
let lastUpdated
90
98
try {
91
99
lastUpdated =
0 commit comments