Skip to content

Commit 0773ed4

Browse files
authored
Refactor api.data.ts: Add comments and improve code clarity (vuejs#3098)
- Added comments to explain the purpose and functionality of key functions. - Clarified the logic behind header parsing, slug generation, and file caching. - Documented the use of regular expressions for header cleaning and anchor extraction. - Improved readability and maintainability of the codebase.
1 parent 1cea431 commit 0773ed4

File tree

1 file changed

+88
-58
lines changed

1 file changed

+88
-58
lines changed

src/api/api.data.ts

Lines changed: 88 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
// api.data.ts
2-
// a file ending with data.(j|t)s will be evaluated in Node.js
32
import fs from 'fs'
43
import path from 'path'
54
import type { MultiSidebarConfig } from '@vue/theme/src/vitepress/config.ts'
65
import { sidebar } from '../../.vitepress/config'
76

7+
// Interface defining the structure of a single header in the API
88
interface APIHeader {
99
anchor: string
1010
text: string
1111
}
1212

13+
// Interface defining the structure of an API group with text, anchor, and items
1314
export 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
2425
export 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(/<sup class=.*/, '')
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(/<sup class=.*/, '') // 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

Comments
 (0)