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.webpackContext + raw loader 加载 try { const context = import.meta.webpackContext("../../content", { recursive: true, regExp: /\.md$/, }); const keys = context.keys(); const index: Record = {}; for (const key of keys) { // context 返回的是模块,需要访问其 default 导出(raw-loader 处理后的内容) const module = context(key) as { default?: string } | string; const content = typeof module === "string" ? module : module.default ?? ""; const normalizedPath = "/content" + key.slice(1); index[normalizedPath] = content; } dataIndex = { ...dataIndex, ...index }; } catch (e) { // webpackContext 不可用时忽略 } })(); 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; } }