diff --git a/src/api/api.data.ts b/src/api/api.data.ts index 719054fd1b..5c4f023b35 100644 --- a/src/api/api.data.ts +++ b/src/api/api.data.ts @@ -1,15 +1,16 @@ // api.data.ts -// a file ending with data.(j|t)s will be evaluated in Node.js import fs from 'fs' import path from 'path' import type { MultiSidebarConfig } from '@vue/theme/src/vitepress/config.ts' import { sidebar } from '../../.vitepress/config' +// Interface defining the structure of a single header in the API interface APIHeader { anchor: string text: string } +// Interface defining the structure of an API group with text, anchor, and items export interface APIGroup { text: string anchor: string @@ -20,79 +21,108 @@ export interface APIGroup { }[] } -// declare resolved data type +// Declare the resolved data type for API groups export declare const data: APIGroup[] -export default { - // declare files that should trigger HMR - watch: './*.md', - // read from fs and generate the data - load(): APIGroup[] { - return (sidebar as MultiSidebarConfig)['/api/'].map((group) => ({ - text: group.text, - anchor: slugify(group.text), - items: group.items.map((item) => ({ - ...item, - headers: parsePageHeaders(item.link) - })) - })) - } +// Utility function to generate a slug from a string (used for anchor links) +function slugify(text: string): string { + return ( + text + // Replace special characters and spaces with hyphens + .replace(/[\s~`!@#$%^&*()\-_+=[\]{}|\\;:"'<>,.?/]+/g, '-') + // Remove continuous separators + .replace(/-{2,}/g, '-') + // Remove leading/trailing hyphens + .replace(/^-+|-+$/g, '') + // Ensure it doesn't start with a number (e.g. #121) + .replace(/^(\d)/, '_$1') + // Convert to lowercase + .toLowerCase() + ) } -const headersCache = new Map< - string, - { - headers: APIHeader[] - timestamp: number - } ->() - -function parsePageHeaders(link: string) { - const fullPath = path.join(__dirname, '../', link) + '.md' - const timestamp = fs.statSync(fullPath).mtimeMs +// Utility function to parse headers from a markdown file at a given link +function parsePageHeaders(link: string): APIHeader[] { + const fullPath = path.join(__dirname, '../', link) + '.md' // Resolve the full file path + const timestamp = fs.statSync(fullPath).mtimeMs // Get the last modified timestamp of the file + // Check if the file is cached and if its timestamp matches const cached = headersCache.get(fullPath) if (cached && timestamp === cached.timestamp) { - return cached.headers + return cached.headers // Return cached headers if they're up-to-date } - const src = fs.readFileSync(fullPath, 'utf-8') - const h2s = src.match(/^## [^\n]+/gm) + const src = fs.readFileSync(fullPath, 'utf-8') // Read the markdown file + const headers = extractHeadersFromMarkdown(src) // Extract headers from the file content + + // Store the extracted headers along with the file's timestamp in the cache + headersCache.set(fullPath, { + timestamp, + headers + }) + + return headers +} + +// Helper function to extract all headers (h2) from markdown content +function extractHeadersFromMarkdown(src: string): APIHeader[] { + const h2s = src.match(/^## [^\n]+/gm) // Match all h2 headers (## header) + const anchorRE = /\{#([^}]+)\}/ // Regular expression to match the anchor link in header (e.g. {#some-anchor}) let headers: APIHeader[] = [] + if (h2s) { - const anchorRE = /\{#([^}]+)\}/ + // Process each h2 header and extract text and anchor headers = h2s.map((h) => { - const text = h - .slice(2) - .replace(/