From e262a83aacda2046b0f63b28c7e804a1de3ba20d Mon Sep 17 00:00:00 2001 From: hypercross Date: Thu, 26 Feb 2026 14:30:09 +0800 Subject: [PATCH] fix: index loading --- src/components/Sidebar.tsx | 4 +- src/data-loader/index.ts | 101 +++++++++++++++++-------------------- 2 files changed, 49 insertions(+), 56 deletions(-) diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 5da73ab..f433984 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -125,8 +125,8 @@ export const Sidebar: Component = (props) => { [], ); - onMount(() => { - const toc = generateToc(); + onMount(async () => { + const toc = await generateToc(); setFileTree(toc.fileTree); setPathHeadings(toc.pathHeadings); }); diff --git a/src/data-loader/index.ts b/src/data-loader/index.ts index d513f41..2d4f4aa 100644 --- a/src/data-loader/index.ts +++ b/src/data-loader/index.ts @@ -3,8 +3,7 @@ import { buildFileTree, extractHeadings, extractSection, type TocNode, type File export { TocNode, FileNode, extractHeadings, buildFileTree, extractSection }; let dataIndex: Record | null = null; -let isDevIndexLoaded = false; -let isCliIndexLoaded = false; +let indexLoadPromise: Promise | null = null; /** * 从索引获取数据 @@ -24,10 +23,54 @@ export function getIndexedPaths(): string[] { 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 function generateToc(): { fileTree: FileNode[]; pathHeadings: Record } { +export async function generateToc(): Promise<{ fileTree: FileNode[]; pathHeadings: Record }> { + await ensureIndexLoaded(); + const paths = getIndexedPaths(); const fileTree = buildFileTree(paths); const pathHeadings: Record = {}; @@ -42,63 +85,13 @@ export function generateToc(): { fileTree: FileNode[]; pathHeadings: Record { - if (isDevIndexLoaded) return; - isDevIndexLoaded = true; - - // @ts-ignore - import.meta.glob 在构建时会被处理 - if (typeof import.meta !== "undefined" && import.meta.glob) { - try { - // @ts-ignore - 只加载 .csv 和 .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 不可用时忽略 - } - } -} - -/** - * 在 CLI 环境从 /__CONTENT_INDEX.json 加载索引 - */ -async function loadCliIndex(): Promise { - if (isCliIndexLoaded) return; - isCliIndexLoaded = true; - - try { - const response = await fetch("/__CONTENT_INDEX.json"); - if (!response.ok) { - throw new Error("Failed to fetch content index"); - } - const index = await response.json(); - dataIndex = { ...dataIndex, ...index }; - } catch (e) { - // CLI 索引不可用时忽略(可能不在 CLI 环境) - } -} - /** * 异步加载数据 * @param path 数据路径 * @returns 数据内容 */ export async function fetchData(path: string): Promise { - // CLI 环境:先从 /__CONTENT_INDEX.json 加载索引 - await loadCliIndex(); - - // dev 环境:加载 glob 索引 - await loadDevIndex(); + await ensureIndexLoaded(); // 首先尝试从索引获取 const indexedData = getIndexedData(path);