chore(docs): eslint migration + extractor updates

Migrate docs to eslint flat config (build-script console override); doc
extractor points at configs/eslint.
This commit is contained in:
2026-06-07 16:30:14 +07:00
parent 23a2795523
commit 96ac895f7a
30 changed files with 1934 additions and 864 deletions
+104 -23
View File
@@ -1,5 +1,37 @@
import metadata from '#docs/metadata';
import type { DocsMetadata, PackageMeta, CategoryMeta, ItemMeta } from '../../modules/extractor/types';
import type {
CategoryMeta,
ComponentMeta,
DocsMetadata,
GuideSection,
ItemMeta,
PackageGroup,
PackageMeta,
} from '../../modules/extractor/types';
/** A unified, normalised entry for any documented leaf, regardless of kind. */
export type DocEntry
= | { kind: 'api'; pkg: PackageMeta; category: CategoryMeta; item: ItemMeta }
| { kind: 'components'; pkg: PackageMeta; component: ComponentMeta }
| { kind: 'guide'; pkg: PackageMeta; section: GuideSection };
export interface SearchResult {
pkg: PackageMeta;
slug: string;
name: string;
description: string;
/** Display kind for the badge */
badge: string;
}
const GROUP_LABELS: Record<PackageGroup, string> = {
core: 'Core',
vue: 'Vue',
configs: 'Configs',
infra: 'Infra',
};
const GROUP_ORDER: PackageGroup[] = ['core', 'vue', 'configs', 'infra'];
export function useDocs() {
const data = metadata as unknown as DocsMetadata;
@@ -8,35 +40,85 @@ export function useDocs() {
return data.packages;
}
/** Packages grouped & ordered for sidebar / landing. */
function getGroupedPackages(): Array<{ group: PackageGroup; label: string; packages: PackageMeta[] }> {
return GROUP_ORDER
.map(group => ({
group,
label: GROUP_LABELS[group],
packages: data.packages.filter(p => p.group === group),
}))
.filter(g => g.packages.length > 0);
}
function getPackage(slug: string): PackageMeta | undefined {
return data.packages.find(p => p.slug === slug);
}
function getItem(packageSlug: string, itemSlug: string): { pkg: PackageMeta; category: CategoryMeta; item: ItemMeta } | undefined {
/** Number of documented leaves in a package, whatever its kind. */
function countEntries(pkg: PackageMeta): number {
if (pkg.kind === 'api') return pkg.categories.reduce((s, c) => s + c.items.length, 0);
if (pkg.kind === 'components') return pkg.components.length;
return pkg.sections.length;
}
/** Resolve any `/:package/:slug` route to a normalised entry. */
function resolveEntry(packageSlug: string, slug: string): DocEntry | undefined {
const pkg = getPackage(packageSlug);
if (!pkg) return undefined;
for (const category of pkg.categories) {
const item = category.items.find(i => i.slug === itemSlug);
if (item) return { pkg, category, item };
if (pkg.kind === 'api') {
for (const category of pkg.categories) {
const item = category.items.find(i => i.slug === slug);
if (item) return { kind: 'api', pkg, category, item };
}
}
else if (pkg.kind === 'components') {
const component = pkg.components.find(c => c.slug === slug);
if (component) return { kind: 'components', pkg, component };
}
else {
const section = pkg.sections.find(s => s.slug === slug);
if (section) return { kind: 'guide', pkg, section };
}
return undefined;
}
function searchItems(query: string): Array<{ pkg: PackageMeta; item: ItemMeta }> {
if (!query.trim()) return [];
const q = query.toLowerCase();
const results: Array<{ pkg: PackageMeta; item: ItemMeta }> = [];
/** The default entry to open when landing on a package, if any. */
function firstEntrySlug(pkg: PackageMeta): string | undefined {
if (pkg.kind === 'api') return pkg.categories[0]?.items[0]?.slug;
if (pkg.kind === 'components') return pkg.components[0]?.slug;
return pkg.sections[0]?.slug;
}
function search(query: string): SearchResult[] {
const q = query.trim().toLowerCase();
if (!q) return [];
const results: SearchResult[] = [];
for (const pkg of data.packages) {
for (const category of pkg.categories) {
for (const item of category.items) {
if (
item.name.toLowerCase().includes(q)
|| item.description.toLowerCase().includes(q)
) {
results.push({ pkg, item });
if (pkg.kind === 'api') {
for (const category of pkg.categories) {
for (const item of category.items) {
if (item.name.toLowerCase().includes(q) || item.description.toLowerCase().includes(q)) {
results.push({ pkg, slug: item.slug, name: item.name, description: item.description, badge: item.kind });
}
}
}
}
else if (pkg.kind === 'components') {
for (const c of pkg.components) {
if (c.name.toLowerCase().includes(q) || c.description.toLowerCase().includes(q)) {
results.push({ pkg, slug: c.slug, name: c.name, description: c.description || `${c.parts.length} parts`, badge: 'component' });
}
}
}
else {
for (const s of pkg.sections) {
if (s.title.toLowerCase().includes(q) || s.markdown.toLowerCase().includes(q)) {
results.push({ pkg, slug: s.slug, name: s.title, description: pkg.name, badge: 'guide' });
}
}
}
@@ -46,19 +128,18 @@ export function useDocs() {
}
function getTotalItems(): number {
return data.packages.reduce(
(sum, pkg) => sum + pkg.categories.reduce(
(catSum, cat) => catSum + cat.items.length, 0,
), 0,
);
return data.packages.reduce((sum, pkg) => sum + countEntries(pkg), 0);
}
return {
data,
getPackages,
getGroupedPackages,
getPackage,
getItem,
searchItems,
countEntries,
resolveEntry,
firstEntrySlug,
search,
getTotalItems,
};
}