11// api.data.ts
2- // a file ending with data.(j|t)s will be evaluated in Node.js
32import fs from 'fs'
43import path from 'path'
54import type { MultiSidebarConfig } from '@vue/theme/src/vitepress/config.ts'
65import { sidebar } from '../../.vitepress/config'
76
7+ // Interface defining the structure of a single header in the API
88interface APIHeader {
99 anchor : string
1010 text : string
1111}
1212
13+ // Interface defining the structure of an API group with text, anchor, and items
1314export interface APIGroup {
1415 text : string
1516 anchor : string
@@ -20,79 +21,108 @@ export interface APIGroup {
2021 } [ ]
2122}
2223
23- // declare resolved data type
24+ // Declare the resolved data type for API groups
2425export declare const data : APIGroup [ ]
2526
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+ )
4042}
4143
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
5348
49+ // Check if the file is cached and if its timestamp matches
5450 const cached = headersCache . get ( fullPath )
5551 if ( cached && timestamp === cached . timestamp ) {
56- return cached . headers
52+ return cached . headers // Return cached headers if they're up-to-date
5753 }
5854
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})
6171 let headers : APIHeader [ ] = [ ]
72+
6273 if ( h2s ) {
63- const anchorRE = / \{ # ( [ ^ } ] + ) \} /
74+ // Process each h2 header and extract text and anchor
6475 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
7378 return { text, anchor }
7479 } )
7580 }
76- headersCache . set ( fullPath , {
77- timestamp,
78- headers
79- } )
81+
8082 return headers
8183}
8284
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+ }
98128}
0 commit comments