import { buildFileTree, extractHeadings, extractSection, type TocNode, type FileNode } from "./toc"; export { TocNode, FileNode, extractHeadings, buildFileTree, extractSection }; let dataIndex: Record | null = null; let indexLoadPromise: Promise | null = null; /** * 从索引获取数据 */ function getIndexedData(path: string): string | null { if (dataIndex && dataIndex[path]) { return dataIndex[path]; } return null; } /** * 获取所有索引路径 */ export function getIndexedPaths(): string[] { if (!dataIndex) return []; return Object.keys(dataIndex); } /** * 加载索引(只加载一次) */ export function ensureIndexLoaded(): Promise { if (indexLoadPromise) return indexLoadPromise; indexLoadPromise = (async () => { // 尝试 CLI 环境:从 /__CONTENT_INDEX.json 加载 try { const response = await fetch("/__CONTENT_INDEX.json"); if (response.ok) { const index = await response.json(); dataIndex = { ...dataIndex, ...index }; return; } } catch (e) { // CLI 索引不可用时尝试 dev 环境 } // Dev 环境:使用 import.meta.glob 加载 if (typeof import.meta !== "undefined" && import.meta.glob) { try { // @ts-ignore - 只加载 .md 文件 const modules = import.meta.glob("../../content/**/*.md", { as: "raw", eager: true, }); const index: Record = {}; for (const [path, content] of Object.entries(modules)) { const normalizedPath = path.replace("/src/", "/"); index[normalizedPath] = content as string; } dataIndex = { ...dataIndex, ...index }; } catch (e) { // glob 不可用时忽略 } } })(); return indexLoadPromise; } /** * 生成目录树(文件树 + 标题结构) */ export async function generateToc(): Promise<{ fileTree: FileNode[]; pathHeadings: Record }> { await ensureIndexLoaded(); const paths = getIndexedPaths(); const fileTree = buildFileTree(paths); const pathHeadings: Record = {}; for (const path of paths) { const content = getIndexedData(path); if (content) { pathHeadings[path] = extractHeadings(content); } } return { fileTree, pathHeadings }; } /** * 异步加载数据 * @param path 数据路径 * @returns 数据内容 */ export async function fetchData(path: string): Promise { await ensureIndexLoaded(); // 首先尝试从索引获取 const indexedData = getIndexedData(path); if (indexedData) { return indexedData; } // 索引不存在时,使用 fetch 加载 if (dataIndex !== null) throw new Error(`no data in index: ${path}`); try { const response = await fetch(path); if (!response.ok) { throw new Error(`Failed to fetch: ${path}`); } return await response.text(); } catch (error) { console.error("fetchData error:", error); throw error; } }