1
1
// api.data.ts
2
- // a file ending with data.(j|t)s will be evaluated in Node.js
3
2
import fs from 'fs'
4
3
import path from 'path'
5
4
import type { MultiSidebarConfig } from '@vue/theme/src/vitepress/config.ts'
6
5
import { sidebar } from '../../.vitepress/config'
7
6
7
+ // Interface defining the structure of a single header in the API
8
8
interface APIHeader {
9
9
anchor : string
10
10
text : string
11
11
}
12
12
13
+ // Interface defining the structure of an API group with text, anchor, and items
13
14
export interface APIGroup {
14
15
text : string
15
16
anchor : string
@@ -20,79 +21,108 @@ export interface APIGroup {
20
21
} [ ]
21
22
}
22
23
23
- // declare resolved data type
24
+ // Declare the resolved data type for API groups
24
25
export declare const data : APIGroup [ ]
25
26
26
- export default {
27
- // declare files that should trigger HMR
28
- watch : './*.md' ,
29
- // read from fs and generate the data
30
- load ( ) : APIGroup [ ] {
31
- return ( sidebar as MultiSidebarConfig ) [ '/api/' ] . map ( ( group ) => ( {
32
- text : group . text ,
33
- anchor : slugify ( group . text ) ,
34
- items : group . items . map ( ( item ) => ( {
35
- ...item ,
36
- headers : parsePageHeaders ( item . link )
37
- } ) )
38
- } ) )
39
- }
27
+ // Utility function to generate a slug from a string (used for anchor links)
28
+ function slugify ( text : string ) : string {
29
+ return (
30
+ text
31
+ // Replace special characters and spaces with hyphens
32
+ . replace ( / [ \s ~ ` ! @ # $ % ^ & * ( ) \- _ + = [ \] { } | \\ ; : " ' < > , . ? / ] + / g, '-' )
33
+ // Remove continuous separators
34
+ . replace ( / - { 2 , } / g, '-' )
35
+ // Remove leading/trailing hyphens
36
+ . replace ( / ^ - + | - + $ / g, '' )
37
+ // Ensure it doesn't start with a number (e.g. #121)
38
+ . replace ( / ^ ( \d ) / , '_$1' )
39
+ // Convert to lowercase
40
+ . toLowerCase ( )
41
+ )
40
42
}
41
43
42
- const headersCache = new Map <
43
- string ,
44
- {
45
- headers : APIHeader [ ]
46
- timestamp : number
47
- }
48
- > ( )
49
-
50
- function parsePageHeaders ( link : string ) {
51
- const fullPath = path . join ( __dirname , '../' , link ) + '.md'
52
- const timestamp = fs . statSync ( fullPath ) . mtimeMs
44
+ // Utility function to parse headers from a markdown file at a given link
45
+ function parsePageHeaders ( link : string ) : APIHeader [ ] {
46
+ const fullPath = path . join ( __dirname , '../' , link ) + '.md' // Resolve the full file path
47
+ const timestamp = fs . statSync ( fullPath ) . mtimeMs // Get the last modified timestamp of the file
53
48
49
+ // Check if the file is cached and if its timestamp matches
54
50
const cached = headersCache . get ( fullPath )
55
51
if ( cached && timestamp === cached . timestamp ) {
56
- return cached . headers
52
+ return cached . headers // Return cached headers if they're up-to-date
57
53
}
58
54
59
- const src = fs . readFileSync ( fullPath , 'utf-8' )
60
- const h2s = src . match ( / ^ # # [ ^ \n ] + / gm)
55
+ const src = fs . readFileSync ( fullPath , 'utf-8' ) // Read the markdown file
56
+ const headers = extractHeadersFromMarkdown ( src ) // Extract headers from the file content
57
+
58
+ // Store the extracted headers along with the file's timestamp in the cache
59
+ headersCache . set ( fullPath , {
60
+ timestamp,
61
+ headers
62
+ } )
63
+
64
+ return headers
65
+ }
66
+
67
+ // Helper function to extract all headers (h2) from markdown content
68
+ function extractHeadersFromMarkdown ( src : string ) : APIHeader [ ] {
69
+ const h2s = src . match ( / ^ # # [ ^ \n ] + / gm) // Match all h2 headers (## header)
70
+ const anchorRE = / \{ # ( [ ^ } ] + ) \} / // Regular expression to match the anchor link in header (e.g. {#some-anchor})
61
71
let headers : APIHeader [ ] = [ ]
72
+
62
73
if ( h2s ) {
63
- const anchorRE = / \{ # ( [ ^ } ] + ) \} /
74
+ // Process each h2 header and extract text and anchor
64
75
headers = h2s . map ( ( h ) => {
65
- const text = h
66
- . slice ( 2 )
67
- . replace ( / < s u p c l a s s = .* / , '' )
68
- . replace ( / \\ < / g, '<' )
69
- . replace ( / ` ( [ ^ ` ] + ) ` / g, '$1' )
70
- . replace ( anchorRE , '' ) // hidden anchor tag
71
- . trim ( )
72
- const anchor = h . match ( anchorRE ) ?. [ 1 ] ?? slugify ( text )
76
+ const text = cleanHeaderText ( h , anchorRE ) // Clean up header text
77
+ const anchor = extractAnchor ( h , anchorRE , text ) // Extract or generate anchor
73
78
return { text, anchor }
74
79
} )
75
80
}
76
- headersCache . set ( fullPath , {
77
- timestamp,
78
- headers
79
- } )
81
+
80
82
return headers
81
83
}
82
84
83
- // same as vitepress' slugify logic
84
- function slugify ( text : string ) : string {
85
- return (
86
- text
87
- // Replace special characters
88
- . replace ( / [ \s ~ ` ! @ # $ % ^ & * ( ) \- _ + = [ \] { } | \\ ; : " ' < > , . ? / ] + / g, '-' )
89
- // Remove continuous separators
90
- . replace ( / - { 2 , } / g, '-' )
91
- // Remove prefixing and trailing separators
92
- . replace ( / ^ - + | - + $ / g, '' )
93
- // ensure it doesn't start with a number (#121)
94
- . replace ( / ^ ( \d ) / , '_$1' )
95
- // lowercase
96
- . toLowerCase ( )
97
- )
85
+ // Helper function to clean up header text (e.g., remove superscript, code formatting)
86
+ function cleanHeaderText ( h : string , anchorRE : RegExp ) : string {
87
+ return h
88
+ . slice ( 2 ) // Remove the "##" part of the header
89
+ . replace ( / < s u p c l a s s = .* / , '' ) // Remove superscript (e.g., <sup> tags)
90
+ . replace ( / \\ < / g, '<' ) // Decode escaped characters like \<
91
+ . replace ( / ` ( [ ^ ` ] + ) ` / g, '$1' ) // Remove inline code formatting (e.g., `code`)
92
+ . replace ( anchorRE , '' ) // Remove anchor tags (e.g., {#anchor})
93
+ . trim ( ) // Trim leading/trailing whitespace
94
+ }
95
+
96
+ // Helper function to extract the anchor link from a header (or generate one if missing)
97
+ function extractAnchor ( h : string , anchorRE : RegExp , text : string ) : string {
98
+ const anchorMatch = h . match ( anchorRE ) // Match anchor if it exists
99
+ return anchorMatch ?. [ 1 ] ?? slugify ( text ) // If no anchor, generate one using slugify
100
+ }
101
+
102
+ // Cache for storing headers and their associated timestamps to avoid re-reading files
103
+ const headersCache = new Map <
104
+ string ,
105
+ {
106
+ headers : APIHeader [ ]
107
+ timestamp : number
108
+ }
109
+ > ( )
110
+
111
+ // Main export function for loading the API data
112
+ export default {
113
+ // Declare files that should trigger Hot Module Replacement (HMR)
114
+ watch : './*.md' ,
115
+
116
+ // Load API data and process sidebar items
117
+ load ( ) : APIGroup [ ] {
118
+ // Generate the API group data by processing the sidebar configuration
119
+ return ( sidebar as MultiSidebarConfig ) [ '/api/' ] . map ( ( group ) => ( {
120
+ text : group . text , // Text of the group (e.g., 'API')
121
+ anchor : slugify ( group . text ) , // Generate anchor for the group title
122
+ items : group . items . map ( ( item ) => ( {
123
+ ...item , // Spread the original item properties
124
+ headers : parsePageHeaders ( item . link ) , // Parse the headers from the item's markdown link
125
+ } ) )
126
+ } ) )
127
+ }
98
128
}
0 commit comments