2026-06-30 18:47:14 +08:00
|
|
|
import yaml from "js-yaml";
|
|
|
|
|
|
2026-06-25 21:04:53 +08:00
|
|
|
export interface DocEntry {
|
|
|
|
|
tag: string;
|
|
|
|
|
icon: string;
|
|
|
|
|
title: string;
|
|
|
|
|
description: string;
|
|
|
|
|
syntax: string;
|
|
|
|
|
props: { name: string; type: string; default?: string; desc: string }[];
|
2026-06-30 18:47:14 +08:00
|
|
|
/** Markdown body (everything after frontmatter ---) */
|
|
|
|
|
body: string;
|
2026-06-25 21:04:53 +08:00
|
|
|
}
|
|
|
|
|
|
2026-06-30 18:47:14 +08:00
|
|
|
/** Splits frontmatter and markdown body from a raw .md string. */
|
|
|
|
|
function parseFrontmatter(raw: string): Record<string, unknown> | null {
|
|
|
|
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
|
|
|
if (!match) return null;
|
|
|
|
|
try {
|
|
|
|
|
return yaml.load(match[1]) as Record<string, unknown>;
|
|
|
|
|
} catch {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-06-25 21:04:53 +08:00
|
|
|
|
2026-06-30 18:47:14 +08:00
|
|
|
function getBody(raw: string): string {
|
|
|
|
|
const match = raw.match(/^---\n[\s\S]*?\n---\n?([\s\S]*)$/);
|
|
|
|
|
return match ? match[1] : raw;
|
|
|
|
|
}
|
2026-06-25 21:04:53 +08:00
|
|
|
|
2026-06-30 18:47:14 +08:00
|
|
|
/** Glob import all .md files in src/doc-entries/ */
|
|
|
|
|
const modules = import.meta.webpackContext("./", {
|
|
|
|
|
recursive: false,
|
|
|
|
|
regExp: /\.md$/,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/** Lazy-loaded doc entries, parsed on first access. */
|
|
|
|
|
let _entries: DocEntry[] | null = null;
|
|
|
|
|
|
|
|
|
|
export function loadDocEntries(): DocEntry[] {
|
|
|
|
|
if (_entries) return _entries;
|
|
|
|
|
|
|
|
|
|
const entries: DocEntry[] = [];
|
|
|
|
|
|
|
|
|
|
for (const key of modules.keys()) {
|
|
|
|
|
const mod = modules(key);
|
|
|
|
|
const raw =
|
|
|
|
|
typeof mod === "string"
|
|
|
|
|
? mod
|
|
|
|
|
: ((mod as { default?: string }).default ?? "");
|
|
|
|
|
if (!raw) continue;
|
|
|
|
|
|
|
|
|
|
const fm = parseFrontmatter(raw);
|
|
|
|
|
if (!fm) continue;
|
|
|
|
|
|
|
|
|
|
const body = getBody(raw);
|
|
|
|
|
|
|
|
|
|
entries.push({
|
|
|
|
|
tag: fm.tag as string,
|
|
|
|
|
icon: fm.icon as string,
|
|
|
|
|
title: fm.title as string,
|
|
|
|
|
description: fm.description as string,
|
|
|
|
|
syntax: fm.syntax as string,
|
|
|
|
|
props: (fm.props as DocEntry["props"]) ?? [],
|
|
|
|
|
body,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Sort alphabetically by tag
|
|
|
|
|
entries.sort((a, b) => a.tag.localeCompare(b.tag));
|
|
|
|
|
_entries = entries;
|
|
|
|
|
return entries;
|
|
|
|
|
}
|
2026-06-25 21:04:53 +08:00
|
|
|
|
2026-06-30 18:47:14 +08:00
|
|
|
/**
|
|
|
|
|
* Direct static array for the DocDialog sidebar — uses webpackContext
|
|
|
|
|
* so all .md files in doc-entries/ are bundled automatically.
|
|
|
|
|
*/
|
|
|
|
|
export const docEntries = loadDocEntries();
|