b3a7f98e5a
CI / E2E Cloudflare (4/8) (push) Failing after 0s
CI / E2E Cloudflare (8/8) (push) Failing after 1s
CI / Lint (push) Failing after 1s
Auto Extract / Extract (push) Failing after 4s
CI / Version Check (push) Failing after 9s
CI / Integration Tests (push) Failing after 1s
CI / E2E tests (1/8) (push) Failing after 1s
CI / E2E tests (2/8) (push) Failing after 2s
CI / E2E tests (3/8) (push) Failing after 2s
CI / Browser Tests (push) Failing after 1s
CI / E2E tests (5/8) (push) Failing after 1s
CI / E2E Cloudflare (2/8) (push) Failing after 2s
CI / Typecheck (push) Failing after 1s
CI / Changeset Validation (push) Failing after 2s
CI / E2E Cloudflare (5/8) (push) Failing after 1s
CI / E2E Cloudflare (6/8) (push) Failing after 1s
CI / E2E Cloudflare (7/8) (push) Failing after 1s
CodeQL / Analyze (javascript-typescript) (push) Failing after 1s
Format / Format (push) Failing after 0s
CodeQL / Analyze (actions) (push) Failing after 4s
CI / E2E tests (4/8) (push) Failing after 1s
CI / E2E tests (6/8) (push) Failing after 1s
CI / E2E tests (7/8) (push) Failing after 2s
CI / E2E tests (8/8) (push) Failing after 1s
CI / E2E Cloudflare (1/8) (push) Failing after 1s
CI / E2E Cloudflare (3/8) (push) Failing after 2s
Preview Releases / Publish Preview (push) Failing after 0s
zizmor / Run zizmor (push) Failing after 1s
Release / Release (push) Failing after 2s
CI / Smoke Tests (push) Failing after 5m36s
CI / Tests (push) Failing after 6m36s
Release / Sync Templates (push) Has been skipped
CI / E2E Tests (push) Has been cancelled
94 lines
2.8 KiB
TypeScript
94 lines
2.8 KiB
TypeScript
import type { APIRoute } from "astro";
|
|
import { getCollection, getEntry } from "astro:content";
|
|
import config from "virtual:starlight/user-config";
|
|
|
|
export const prerender = true;
|
|
|
|
type SidebarItem = NonNullable<typeof config.sidebar>[number];
|
|
type DocsEntry = Awaited<ReturnType<typeof getCollection<"docs">>>[number];
|
|
type Link = { label: string; href: string };
|
|
|
|
const TRIM_SLASHES = /^\/|\/$/g;
|
|
const ABSOLUTE_URL = /^https?:\/\//;
|
|
|
|
export const GET: APIRoute = async ({ site }) => {
|
|
// Pre-load the docs collection so autogenerated groups can resolve
|
|
// without one `getEntry` call per page.
|
|
const docs = await getCollection("docs");
|
|
|
|
const lang = config.defaultLocale.lang;
|
|
const title = (lang ? config.title[lang] : undefined) ?? Object.values(config.title)[0] ?? "";
|
|
const lines: string[] = [`# ${title}`];
|
|
if (config.tagline) {
|
|
lines.push("", `> ${config.tagline}`);
|
|
}
|
|
|
|
for (const item of config.sidebar ?? []) {
|
|
const links = await resolveItem(item, docs);
|
|
if (links.length === 0) continue;
|
|
|
|
const groupLabel = typeof item !== "string" && "label" in item ? item.label : undefined;
|
|
if (groupLabel && typeof item !== "string" && ("items" in item || "autogenerate" in item)) {
|
|
lines.push("", `## ${groupLabel}`, "");
|
|
} else {
|
|
lines.push("");
|
|
}
|
|
|
|
for (const link of links) {
|
|
lines.push(`- [${link.label}](${absUrl(link.href, site)})`);
|
|
}
|
|
}
|
|
|
|
return new Response(lines.join("\n") + "\n", {
|
|
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
|
});
|
|
};
|
|
|
|
async function resolveItem(item: SidebarItem, docs: DocsEntry[]): Promise<Link[]> {
|
|
// Shorthand: a string is a slug reference.
|
|
if (typeof item === "string") {
|
|
const entry = getEntry("docs", item);
|
|
return entry ? [entryLink(entry)] : [];
|
|
}
|
|
|
|
if ("link" in item && item.link) {
|
|
return [{ label: item.label, href: item.link }];
|
|
}
|
|
|
|
if ("slug" in item && item.slug) {
|
|
const entry = await getEntry("docs", item.slug);
|
|
return entry ? [entryLink(entry, item.label)] : [];
|
|
}
|
|
|
|
if ("items" in item && item.items) {
|
|
const out: Link[] = [];
|
|
for (const child of item.items) {
|
|
out.push(...(await resolveItem(child, docs)));
|
|
}
|
|
return out;
|
|
}
|
|
|
|
if ("autogenerate" in item && item.autogenerate) {
|
|
const prefix = item.autogenerate.directory.replace(TRIM_SLASHES, "") + "/";
|
|
return docs
|
|
.filter((entry) => entry.id.startsWith(prefix))
|
|
.toSorted((a, b) => a.id.localeCompare(b.id))
|
|
.map((entry) => entryLink(entry));
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
function entryLink(entry: DocsEntry, override?: string): Link {
|
|
const slug = entry.id === "index" ? "" : entry.id;
|
|
const href = "/" + slug + (slug ? "/" : "");
|
|
const label = override ?? entry.data.sidebar?.label ?? entry.data.title;
|
|
return { label, href };
|
|
}
|
|
|
|
function absUrl(href: string, site: URL | undefined): string {
|
|
if (ABSOLUTE_URL.test(href)) return href;
|
|
if (!site) return href;
|
|
return new URL(href, site).toString();
|
|
}
|