From 3f275f9de12f39bb79a26e6ee20a7c85b5bc94ba Mon Sep 17 00:00:00 2001 From: Marcus Souza Date: Mon, 28 Sep 2026 18:16:33 -0300 Subject: [PATCH 1/2] feat(agents): publish llms.txt per section and a metadata sitemap.md from the nav The docs index was one flat list of every collection file (orphans and the style guide included), with a title and a link and nothing else. The nav model already holds the order, the section, the kind, the products, and the last change, so the index now comes from it: /{lang}/{docs}/llms.txt lists the sections, every section root serves its own llms.txt, /{lang}/{docs}/sitemap.md carries type, section, topics, updated, and summary per page, and /{lang}/docs-llms.txt stays as the full list the agent-setup pages cite. Every page also announces its Markdown twin with a rel=alternate link. --- src/components/HeadSEO.astro | 1 + src/pages/[lang]/[...path]/llms.txt.ts | 65 +++++++ src/pages/[lang]/[docs]/sitemap.md.ts | 60 ++++++ src/pages/[lang]/docs-llms.txt.js | 46 ----- src/pages/[lang]/docs-llms.txt.ts | 38 ++++ src/util/machineIndex.ts | 249 +++++++++++++++++++++++++ 6 files changed, 413 insertions(+), 46 deletions(-) create mode 100644 src/pages/[lang]/[...path]/llms.txt.ts create mode 100644 src/pages/[lang]/[docs]/sitemap.md.ts delete mode 100644 src/pages/[lang]/docs-llms.txt.js create mode 100644 src/pages/[lang]/docs-llms.txt.ts create mode 100644 src/util/machineIndex.ts diff --git a/src/components/HeadSEO.astro b/src/components/HeadSEO.astro index ce19c2f76b..e7055e9f0f 100644 --- a/src/components/HeadSEO.astro +++ b/src/components/HeadSEO.astro @@ -44,6 +44,7 @@ const getAlternateHref = (lang: string, slug: string) => { --- + { translatedPages diff --git a/src/pages/[lang]/[...path]/llms.txt.ts b/src/pages/[lang]/[...path]/llms.txt.ts new file mode 100644 index 0000000000..1618a4139a --- /dev/null +++ b/src/pages/[lang]/[...path]/llms.txt.ts @@ -0,0 +1,65 @@ +import type { APIRoute, GetStaticPaths } from 'astro'; + +import { LANGS, type Lang } from '~/nav/schema'; +import { getMachineIndex, llmsEntry, type IndexedTree, type MachineIndex } from '~/util/machineIndex'; + +/** llms.txt at the docs root (one entry per section) and at every section root (every + * page it lists). The site root belongs to the marketing site, so the docs root is the + * conventional path here. Entries link the Markdown twin of each page. */ + +const SUMMARY: Record = { + en: 'Documentation for the Azion Web Platform: build, store, secure, and observe applications on Azion\'s distributed infrastructure. Every page is served as Markdown at its URL with .md appended, and every section publishes its own llms.txt.', + 'pt-br': + 'Documentação da Azion Web Platform: construa, armazene, proteja e observe aplicações na infraestrutura distribuída da Azion. Toda página é servida como Markdown na própria URL com .md ao final, e toda seção publica o próprio llms.txt.', +}; + +const TITLE: Record = { en: 'Azion documentation', 'pt-br': 'Documentação Azion' }; +const INDEX_NOTE: Record = { en: 'Section index', 'pt-br': 'Índice da seção' }; +const PAGES: Record = { en: 'Pages', 'pt-br': 'Páginas' }; + +function renderRoot(index: MachineIndex): string { + const lines = [`# ${TITLE[index.lang]}`, '', `> ${SUMMARY[index.lang]}`, '']; + for (const group of index.rootGroups) { + lines.push(`## ${group.label ?? group.listed.join(', ')}`, ''); + for (const tree of group.trees) { + const root = tree.pages[0]; + const note = [tree.description, `${INDEX_NOTE[index.lang]}: ${tree.llmsUrl}`] + .filter(Boolean) + .join('. '); + lines.push(llmsEntry(tree.title, root ? root.markdownUrl : tree.llmsUrl, note)); + } + lines.push(''); + } + return lines.join('\n'); +} + +function renderTree(index: MachineIndex, tree: IndexedTree): string { + const lines = [`# ${tree.title}`, '']; + if (tree.description) lines.push(`> ${tree.description}`, ''); + for (const group of tree.groups) { + lines.push(`## ${group.label ?? PAGES[index.lang]}`, ''); + for (const page of group.pages) lines.push(llmsEntry(page.title, page.markdownUrl, page.description)); + lines.push(''); + } + return lines.join('\n'); +} + +export const getStaticPaths = (async () => { + const paths: { params: { lang: Lang; path: string }; props: { tree?: string } }[] = []; + for (const lang of LANGS) { + const index = await getMachineIndex(lang); + paths.push({ params: { lang, path: index.docsBase }, props: {} }); + for (const tree of index.trees) { + paths.push({ params: { lang, path: `${index.docsBase}/${tree.path}` }, props: { tree: tree.id } }); + } + } + return paths; +}) satisfies GetStaticPaths; + +export const GET: APIRoute = async ({ params, props }) => { + const index = await getMachineIndex(params.lang as Lang); + const treeId = (props as { tree?: string }).tree; + const tree = treeId ? index.trees.find((t) => t.id === treeId) : undefined; + const body = tree ? renderTree(index, tree) : renderRoot(index); + return new Response(body, { headers: { 'Content-Type': 'text/markdown; charset=utf-8' } }); +}; diff --git a/src/pages/[lang]/[docs]/sitemap.md.ts b/src/pages/[lang]/[docs]/sitemap.md.ts new file mode 100644 index 0000000000..c2a5e6cfb8 --- /dev/null +++ b/src/pages/[lang]/[docs]/sitemap.md.ts @@ -0,0 +1,60 @@ +import type { APIRoute, GetStaticPaths } from 'astro'; + +import { DOCS_BASE } from '~/nav/resolve'; +import { LANGS, type Lang } from '~/nav/schema'; +import { getMachineIndex } from '~/util/machineIndex'; + +/** sitemap.md at the docs root: one entry per page with the metadata an agent filters + * on before fetching (type, section, topics, last change, summary), all of it already + * held by the nav and the frontmatter. */ + +const LABELS: Record> = { + en: { + title: 'Azion documentation sitemap', + summary: + 'Every documentation page in navigation order. Each entry carries the page type, its section, the products it covers, the date of its last change, and its summary. The URL serves HTML; append .md for Markdown.', + type: 'type', + section: 'section', + topics: 'topics', + updated: 'updated', + summaryKey: 'summary', + markdown: 'markdown', + }, + 'pt-br': { + title: 'Mapa da documentação Azion', + summary: + 'Todas as páginas da documentação na ordem da navegação. Cada entrada traz o tipo da página, a seção, os produtos que ela cobre, a data da última alteração e o resumo. A URL serve HTML; acrescente .md para Markdown.', + type: 'tipo', + section: 'seção', + topics: 'tópicos', + updated: 'atualizado', + summaryKey: 'resumo', + markdown: 'markdown', + }, +}; + +export const getStaticPaths = (() => + LANGS.map((lang) => ({ params: { lang, docs: DOCS_BASE[lang] } }))) satisfies GetStaticPaths; + +export const GET: APIRoute = async ({ params }) => { + const lang = params.lang as Lang; + const index = await getMachineIndex(lang); + const l = LABELS[lang]; + const lines = [`# ${l.title}`, '', `> ${l.summary}`, '']; + for (const tree of index.trees) { + lines.push(`## ${tree.title}`, ''); + for (const page of tree.pages) { + lines.push(`- [${page.title.trim()}](${page.url})`); + if (page.kind) lines.push(` - ${l.type}: ${page.kind}`); + lines.push(` - ${l.section}: ${page.section.join(' > ')}`); + if (page.topics.length) lines.push(` - ${l.topics}: ${page.topics.join(', ')}`); + if (page.updated) lines.push(` - ${l.updated}: ${page.updated}`); + if (page.description) lines.push(` - ${l.summaryKey}: ${page.description.replace(/\s+/g, ' ').trim()}`); + lines.push(` - ${l.markdown}: ${page.markdownUrl}`); + } + lines.push(''); + } + return new Response(lines.join('\n'), { + headers: { 'Content-Type': 'text/markdown; charset=utf-8' }, + }); +}; diff --git a/src/pages/[lang]/docs-llms.txt.js b/src/pages/[lang]/docs-llms.txt.js deleted file mode 100644 index 8328c5b958..0000000000 --- a/src/pages/[lang]/docs-llms.txt.js +++ /dev/null @@ -1,46 +0,0 @@ -import { getCollection } from 'astro:content'; -// import getLangs from '~/util/getLangs' -import { groupPagesByLang } from '~/util/groupPagesByLang'; -import { removeTrailingLeadingSlashs } from '~/util/removeSlashs'; -import { SITE_URL } from '~/consts'; -import { withDocsHome } from '~/data/docs-home'; - -function buildLinks(collection, lang) { - let content = ''; - for (const page of collection[lang]) { - const permalink = removeTrailingLeadingSlashs(page.data.permalink); - const link = `- [${page.data.title.trim()}](${SITE_URL}/${lang}/${permalink}.md)`; - content += link.replace('/.md', '.md') + '\n\n'; - } - return content; -} - -export async function getStaticPaths() { - let docs = await getCollection('docs'); - - return ['en', 'pt-br'].map((lang) => { - return { - params: { - lang: lang, - }, - props: { - docs: withDocsHome(groupPagesByLang(docs)), - }, - }; - }); -} - -export async function GET({ params, props }) { - let content = ''; - const { lang } = params; - const { docs } = props; - - content += `## DOCUMENTATION\n\n${buildLinks(docs, lang)}\n\n\n\n`; - - return new Response(content, { - status: 200, - headers: { - 'Content-Type': 'text/markdown; charset=utf-8', - }, - }); -} diff --git a/src/pages/[lang]/docs-llms.txt.ts b/src/pages/[lang]/docs-llms.txt.ts new file mode 100644 index 0000000000..b02bb88cdd --- /dev/null +++ b/src/pages/[lang]/docs-llms.txt.ts @@ -0,0 +1,38 @@ +import type { APIRoute, GetStaticPaths } from 'astro'; + +import { SITE_URL } from '~/consts'; +import { LANGS, type Lang } from '~/nav/schema'; +import { getMachineIndex, llmsEntry } from '~/util/machineIndex'; + +/** + * The full page list, one section per navigation tree. This path predates the + * per-section files and stays because the agent-setup pages and prompts cite it; + * /{lang}/{docs}/llms.txt is the conventional entry point and links here. + */ + +const TITLE: Record = { + en: 'Azion documentation, every page', + 'pt-br': 'Documentação Azion, todas as páginas', +}; +const SUMMARY: Record = { + en: 'Every page the documentation navigation lists, in sidebar order, linked as Markdown. The section index is at', + 'pt-br': + 'Todas as páginas que a navegação da documentação lista, na ordem do menu, com links em Markdown. O índice de seções está em', +}; + +export const getStaticPaths = (() => LANGS.map((lang) => ({ params: { lang } }))) satisfies GetStaticPaths; + +export const GET: APIRoute = async ({ params }) => { + const lang = params.lang as Lang; + const index = await getMachineIndex(lang); + const sectionIndex = `${SITE_URL}/${lang}/${index.docsBase}/llms.txt`; + const lines = [`# ${TITLE[lang]}`, '', `> ${SUMMARY[lang]} ${sectionIndex}`, '']; + for (const tree of index.trees) { + lines.push(`## ${tree.title}`, ''); + for (const page of tree.pages) lines.push(llmsEntry(page.title, page.markdownUrl, page.description)); + lines.push(''); + } + return new Response(lines.join('\n'), { + headers: { 'Content-Type': 'text/markdown; charset=utf-8' }, + }); +}; diff --git a/src/util/machineIndex.ts b/src/util/machineIndex.ts new file mode 100644 index 0000000000..39a95be28e --- /dev/null +++ b/src/util/machineIndex.ts @@ -0,0 +1,249 @@ +import { SITE_URL } from '~/consts'; +import { getNavData, type Lang } from '~/nav/index'; +import { DOCS_BASE, pageHref, text, type NavData } from '~/nav/resolve'; +import type { NavNode, NavTree } from '~/nav/schema'; + +/** The page inventory behind llms.txt and sitemap.md: every page the navigation lists, + * in sidebar order. Nothing reads the collection directly, so a page in no tree (an + * orphan, the coming-soon page, the style guide) never reaches an index. */ + +export interface IndexedPage { + namespace: string; + title: string; + description?: string; + /** Absolute page URL. */ + url: string; + /** Absolute URL of the page's Markdown twin. */ + markdownUrl: string; + /** The catalog kind, when the row or its slot states one. */ + kind?: string; + /** The products the page is about: the row's products, else the owning product tree. */ + topics: string[]; + /** Last content change, as YYYY-MM-DD, from git. */ + updated?: string; + /** Root group, tree title, then the labels of the row's ancestors. */ + section: string[]; +} + +export interface IndexedGroup { + label?: string; + pages: IndexedPage[]; +} + +export interface IndexedTree { + id: string; + title: string; + description?: string; + /** Tree path under the docs base, such as `build/functions`. */ + path: string; + /** Absolute URL of the tree's own llms.txt. */ + llmsUrl: string; + groups: IndexedGroup[]; + pages: IndexedPage[]; +} + +export interface RootGroup { + label?: string; + /** Titles of the trees the root catalog lists directly; the label fallback for an unlabeled group. */ + listed: string[]; + trees: IndexedTree[]; +} + +export interface MachineIndex { + lang: Lang; + /** `documentation` or `documentacao`. */ + docsBase: string; + rootGroups: RootGroup[]; + trees: IndexedTree[]; + pages: IndexedPage[]; +} + +/** The kind a product-section slot holds, by the slot's English label (information architecture). */ +const SLOT_KIND: Record = { + Quickstart: 'quickstart', + 'How it works': 'concept', + Features: 'reference', + 'Guides and tutorials': 'navigation-hub', + Examples: 'reference', + Reference: 'reference', + Limits: 'reference', + 'Best practices': 'concept', + Troubleshooting: 'troubleshooting', + Glossary: 'glossary', + Changelog: 'changelog', +}; + +const markdownUrl = (url: string) => `${url.replace(/\/$/, '')}.md`; + +/** Trees reachable from the root catalog or the top nav, or descending from one that is. */ +function reachableTrees(data: NavData): string[] { + const listed: string[] = []; + const visit = (items: NavNode[]) => { + for (const item of items) { + if (item.tree && !listed.includes(item.tree)) listed.push(item.tree); + if (item.items) visit(item.items); + } + }; + for (const group of data.root.groups) visit(group.items); + if (data.topnav) { + for (const column of [...data.topnav.products, ...data.topnav.devtools]) visit(column.items); + if (!listed.includes(data.topnav.guides)) listed.push(data.topnav.guides); + } + + const reachable = new Set(listed); + let grew = true; + while (grew) { + grew = false; + for (const tree of data.trees.values()) { + if (!reachable.has(tree.id) && reachable.has(tree.parent)) { + reachable.add(tree.id); + grew = true; + } + } + } + // Listed order first, then the descendants in file order. + return [...listed, ...[...data.trees.keys()].filter((id) => reachable.has(id) && !listed.includes(id))]; +} + +function indexTree(data: NavData, tree: NavTree, lang: Lang, rootLabel?: string): IndexedTree { + const title = text(tree.title, lang) ?? tree.id; + const path = (text(tree.path, lang) ?? tree.id).replace(/^\/|\/$/g, ''); + const groups: IndexedGroup[] = []; + const pages: IndexedPage[] = []; + + const walk = ( + items: NavNode[], + chain: { en?: string; local?: string }[], + inheritedKind: string | undefined, + into: IndexedGroup + ) => { + for (const node of items) { + const label = { en: text(node.label, 'en'), local: text(node.label, lang) }; + const kindHere = node.kind ?? inheritedKind; + if (node.page && !node.linkOnly && !node.placeholder && !node.href) { + const facts = data.pages.get(node.page)?.[lang] ?? data.pages.get(node.page)?.en; + const href = pageHref(data, node.page, lang); + if (facts && href) { + const parentEn = [...chain].reverse().find((c) => c.en)?.en; + const kind = + node.kind ?? + (node.page === tree.root + ? 'overview' + : inheritedKind ?? + (tree.id === 'guides' ? 'tutorial' : SLOT_KIND[label.en ?? ''] ?? SLOT_KIND[parentEn ?? ''])); + const url = `${SITE_URL}${href}`; + const page: IndexedPage = { + namespace: node.page, + title: label.local ?? facts.title ?? node.page, + description: facts.description, + url, + markdownUrl: markdownUrl(url), + kind, + topics: node.products ?? [tree.id], + updated: facts.updated?.slice(0, 10), + section: [rootLabel, title, ...chain.map((c) => c.local)].filter( + (part): part is string => Boolean(part) + ), + }; + into.pages.push(page); + pages.push(page); + } + } + if (node.items?.length) { + const next = label.local ? [...chain, label] : chain; + walk(node.items, next, kindHere, into); + } + } + }; + + for (const group of tree.groups) { + const into: IndexedGroup = { label: text(group.label, lang), pages: [] }; + walk(group.items, [], undefined, into); + if (into.pages.length) groups.push(into); + } + + return { + id: tree.id, + title, + description: text(tree.description, lang), + path, + llmsUrl: `${SITE_URL}/${lang}/${DOCS_BASE[lang]}/${path}/llms.txt`, + groups, + pages, + }; +} + +export async function getMachineIndex(lang: Lang): Promise { + const data = await getNavData(); + const order = reachableTrees(data); + const built = new Map(); + + // Root groups carry the section label; descendants of a listed tree (the developer + // tools) file under the same label as their parent. + const rootGroups: RootGroup[] = []; + const groupOf = new Map(); + for (const group of data.root.groups) { + const entry: RootGroup = { label: text(group.label, lang), listed: [], trees: [] }; + const collect = (items: NavNode[]) => { + for (const item of items) { + if (item.tree) { + groupOf.set(item.tree, entry); + const title = text(data.trees.get(item.tree)?.title, lang); + if (title) entry.listed.push(title); + } + if (item.items) collect(item.items); + } + }; + collect(group.items); + rootGroups.push(entry); + } + // A tree another tree lists as a row (Load Balancer inside Platform) files under that tree's group. + const adopt = (treeId: string, group: RootGroup) => { + const visitRows = (items: NavNode[]) => { + for (const item of items) { + if (item.tree && !groupOf.has(item.tree)) { + groupOf.set(item.tree, group); + adopt(item.tree, group); + } + if (item.items) visitRows(item.items); + } + }; + for (const group of data.trees.get(treeId)?.groups ?? []) visitRows(group.items); + }; + for (const [id, group] of [...groupOf]) adopt(id, group); + // A tree the top nav lists but the root catalog does not (the guides hub) is its own group. + for (const id of order) { + const tree = data.trees.get(id); + if (!tree || groupOf.has(id) || tree.parent !== 'root') continue; + const title = text(tree.title, lang) ?? id; + const entry: RootGroup = { label: title, listed: [title], trees: [] }; + groupOf.set(id, entry); + rootGroups.push(entry); + } + for (const id of order) { + const tree = data.trees.get(id); + if (!tree) continue; + let ancestor = tree.id; + while (!groupOf.has(ancestor) && data.trees.get(ancestor)?.parent && data.trees.get(ancestor)!.parent !== 'root') + ancestor = data.trees.get(ancestor)!.parent; + const group = groupOf.get(ancestor) ?? rootGroups[rootGroups.length - 1]; + const indexed = indexTree(data, tree, lang, group.label ?? group.listed.join(', ')); + built.set(id, indexed); + group.trees.push(indexed); + } + + const trees = order.map((id) => built.get(id)).filter((t): t is IndexedTree => Boolean(t)); + return { + lang, + docsBase: DOCS_BASE[lang], + rootGroups: rootGroups.filter((g) => g.trees.length), + trees, + pages: trees.flatMap((t) => t.pages), + }; +} + +/** `[title](url): description` on one line, as the llms.txt convention states it. */ +export function llmsEntry(title: string, url: string, description?: string): string { + const note = description?.replace(/\s+/g, ' ').trim(); + return `- [${title.trim()}](${url})${note ? `: ${note}` : ''}`; +} From a09f8ea515b7a094b2262d9a55cdbb0f20c848c2 Mon Sep 17 00:00:00 2001 From: Marcus Souza Date: Mon, 28 Sep 2026 18:59:17 -0300 Subject: [PATCH 2/2] style: format the changed files with prettier --- src/pages/[lang]/[...path]/llms.txt.ts | 17 +++++++++++++---- src/pages/[lang]/[docs]/sitemap.md.ts | 3 ++- src/pages/[lang]/docs-llms.txt.ts | 6 ++++-- src/util/machineIndex.ts | 15 ++++++++++++--- 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/pages/[lang]/[...path]/llms.txt.ts b/src/pages/[lang]/[...path]/llms.txt.ts index 1618a4139a..4f2de322be 100644 --- a/src/pages/[lang]/[...path]/llms.txt.ts +++ b/src/pages/[lang]/[...path]/llms.txt.ts @@ -1,14 +1,19 @@ import type { APIRoute, GetStaticPaths } from 'astro'; import { LANGS, type Lang } from '~/nav/schema'; -import { getMachineIndex, llmsEntry, type IndexedTree, type MachineIndex } from '~/util/machineIndex'; +import { + getMachineIndex, + llmsEntry, + type IndexedTree, + type MachineIndex, +} from '~/util/machineIndex'; /** llms.txt at the docs root (one entry per section) and at every section root (every * page it lists). The site root belongs to the marketing site, so the docs root is the * conventional path here. Entries link the Markdown twin of each page. */ const SUMMARY: Record = { - en: 'Documentation for the Azion Web Platform: build, store, secure, and observe applications on Azion\'s distributed infrastructure. Every page is served as Markdown at its URL with .md appended, and every section publishes its own llms.txt.', + en: "Documentation for the Azion Web Platform: build, store, secure, and observe applications on Azion's distributed infrastructure. Every page is served as Markdown at its URL with .md appended, and every section publishes its own llms.txt.", 'pt-br': 'Documentação da Azion Web Platform: construa, armazene, proteja e observe aplicações na infraestrutura distribuída da Azion. Toda página é servida como Markdown na própria URL com .md ao final, e toda seção publica o próprio llms.txt.', }; @@ -38,7 +43,8 @@ function renderTree(index: MachineIndex, tree: IndexedTree): string { if (tree.description) lines.push(`> ${tree.description}`, ''); for (const group of tree.groups) { lines.push(`## ${group.label ?? PAGES[index.lang]}`, ''); - for (const page of group.pages) lines.push(llmsEntry(page.title, page.markdownUrl, page.description)); + for (const page of group.pages) + lines.push(llmsEntry(page.title, page.markdownUrl, page.description)); lines.push(''); } return lines.join('\n'); @@ -50,7 +56,10 @@ export const getStaticPaths = (async () => { const index = await getMachineIndex(lang); paths.push({ params: { lang, path: index.docsBase }, props: {} }); for (const tree of index.trees) { - paths.push({ params: { lang, path: `${index.docsBase}/${tree.path}` }, props: { tree: tree.id } }); + paths.push({ + params: { lang, path: `${index.docsBase}/${tree.path}` }, + props: { tree: tree.id }, + }); } } return paths; diff --git a/src/pages/[lang]/[docs]/sitemap.md.ts b/src/pages/[lang]/[docs]/sitemap.md.ts index c2a5e6cfb8..ede732c19a 100644 --- a/src/pages/[lang]/[docs]/sitemap.md.ts +++ b/src/pages/[lang]/[docs]/sitemap.md.ts @@ -49,7 +49,8 @@ export const GET: APIRoute = async ({ params }) => { lines.push(` - ${l.section}: ${page.section.join(' > ')}`); if (page.topics.length) lines.push(` - ${l.topics}: ${page.topics.join(', ')}`); if (page.updated) lines.push(` - ${l.updated}: ${page.updated}`); - if (page.description) lines.push(` - ${l.summaryKey}: ${page.description.replace(/\s+/g, ' ').trim()}`); + if (page.description) + lines.push(` - ${l.summaryKey}: ${page.description.replace(/\s+/g, ' ').trim()}`); lines.push(` - ${l.markdown}: ${page.markdownUrl}`); } lines.push(''); diff --git a/src/pages/[lang]/docs-llms.txt.ts b/src/pages/[lang]/docs-llms.txt.ts index b02bb88cdd..5ca809fcb7 100644 --- a/src/pages/[lang]/docs-llms.txt.ts +++ b/src/pages/[lang]/docs-llms.txt.ts @@ -20,7 +20,8 @@ const SUMMARY: Record = { 'Todas as páginas que a navegação da documentação lista, na ordem do menu, com links em Markdown. O índice de seções está em', }; -export const getStaticPaths = (() => LANGS.map((lang) => ({ params: { lang } }))) satisfies GetStaticPaths; +export const getStaticPaths = (() => + LANGS.map((lang) => ({ params: { lang } }))) satisfies GetStaticPaths; export const GET: APIRoute = async ({ params }) => { const lang = params.lang as Lang; @@ -29,7 +30,8 @@ export const GET: APIRoute = async ({ params }) => { const lines = [`# ${TITLE[lang]}`, '', `> ${SUMMARY[lang]} ${sectionIndex}`, '']; for (const tree of index.trees) { lines.push(`## ${tree.title}`, ''); - for (const page of tree.pages) lines.push(llmsEntry(page.title, page.markdownUrl, page.description)); + for (const page of tree.pages) + lines.push(llmsEntry(page.title, page.markdownUrl, page.description)); lines.push(''); } return new Response(lines.join('\n'), { diff --git a/src/util/machineIndex.ts b/src/util/machineIndex.ts index 39a95be28e..f31b12548e 100644 --- a/src/util/machineIndex.ts +++ b/src/util/machineIndex.ts @@ -102,7 +102,10 @@ function reachableTrees(data: NavData): string[] { } } // Listed order first, then the descendants in file order. - return [...listed, ...[...data.trees.keys()].filter((id) => reachable.has(id) && !listed.includes(id))]; + return [ + ...listed, + ...[...data.trees.keys()].filter((id) => reachable.has(id) && !listed.includes(id)), + ]; } function indexTree(data: NavData, tree: NavTree, lang: Lang, rootLabel?: string): IndexedTree { @@ -130,7 +133,9 @@ function indexTree(data: NavData, tree: NavTree, lang: Lang, rootLabel?: string) (node.page === tree.root ? 'overview' : inheritedKind ?? - (tree.id === 'guides' ? 'tutorial' : SLOT_KIND[label.en ?? ''] ?? SLOT_KIND[parentEn ?? ''])); + (tree.id === 'guides' + ? 'tutorial' + : SLOT_KIND[label.en ?? ''] ?? SLOT_KIND[parentEn ?? ''])); const url = `${SITE_URL}${href}`; const page: IndexedPage = { namespace: node.page, @@ -224,7 +229,11 @@ export async function getMachineIndex(lang: Lang): Promise { const tree = data.trees.get(id); if (!tree) continue; let ancestor = tree.id; - while (!groupOf.has(ancestor) && data.trees.get(ancestor)?.parent && data.trees.get(ancestor)!.parent !== 'root') + while ( + !groupOf.has(ancestor) && + data.trees.get(ancestor)?.parent && + data.trees.get(ancestor)!.parent !== 'root' + ) ancestor = data.trees.get(ancestor)!.parent; const group = groupOf.get(ancestor) ?? rootGroups[rootGroups.length - 1]; const indexed = indexTree(data, tree, lang, group.label ?? group.listed.join(', '));