Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ Content is Markdown/MDX under `site/src/content/docs/` (`guide/`, `middleware/`,
generated from each page's `sidebar.order` frontmatter. Every page needs a
`title` and `description`.

The build also publishes `/llms.txt` and `/next/llms.txt`. Their page links and
descriptions come from that same content, and each index identifies its pinned
Echo source revision. `site:check` verifies both files and their site links.

## Cookbook recipes

Each folder under `cookbook/` is a self-contained example. Run one with:
Expand Down
13 changes: 13 additions & 0 deletions site/scripts/check-site.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -98,5 +98,18 @@ for (const locale of ['es', 'ja', 'pt-br', 'zh-cn']) {
for (const path of ['sitemap-index.xml', 'pagefind/pagefind.js']) {
if (!existsSync(join(distDir, path))) problems.push(`Missing ${path}`);
}
for (const path of ['llms.txt', 'next/llms.txt']) {
const file = join(distDir, path);
if (!existsSync(file)) { problems.push(`Missing ${path}`); continue; }
const content = readFileSync(file, 'utf8');
if (!content.startsWith('# Echo\n')) problems.push(`${path}: missing Echo heading`);
for (const [, link] of content.matchAll(/\]\((https:\/\/echo\.labstack\.com\/[^)]+)\)/g)) {
const pathname = new URL(link).pathname;
const resolved = resolve(distDir, `.${pathname}`);
const target = extname(resolved) ? resolved : join(resolved, 'index.html');
if (!existsSync(target)) problems.push(`${path}: missing ${link}`);
checked++;
}
}
if (problems.length) throw new Error(`${problems.length} site check failure(s):\n${problems.join('\n')}`);
console.log(`Checked ${routes.length} routes and ${checked} internal links/assets; all resolve`);
61 changes: 61 additions & 0 deletions site/src/pages/llms.txt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { getCollection } from 'astro:content';
import type { APIRoute } from 'astro';
import stableSource from '../../echo-source.json';
import nextSource from '../../next-source.json';

export const prerender = true;

const origin = 'https://echo.labstack.com';
const sections = [
{ title: 'Guide', prefix: 'guide/' },
{ title: 'Middleware', prefix: 'middleware/' },
{ title: 'Cookbook', prefix: 'cookbook/' },
];

export const GET: APIRoute = async () => {
const base = import.meta.env.BASE_URL;
const source = base === '/next/' ? nextSource : stableSource;
const pages = await getCollection('docs');
const englishPages = pages.filter(({ id }) =>
!/^(?:es|ja|pt-br|zh-cn)\//.test(id),
);
const lines = [
'# Echo',
'',
'> Echo is a Go web framework. These docs cover setup, routing, requests, responses, middleware, and runnable examples.',
'',
`This index describes the ${source.release} docs, built against [Echo source](${source.repository.replace(/\.git$/, '')}/tree/${source.revision}). For package API signatures, see [pkg.go.dev](https://pkg.go.dev/github.com/labstack/echo/v5).`,
'',
];

for (const { title, prefix } of sections) {
lines.push(`## ${title}`, '');
const entries = englishPages
.filter(({ id }) => id.startsWith(prefix))
.sort((a, b) =>
(a.data.sidebar?.order ?? 999) - (b.data.sidebar?.order ?? 999) ||
a.data.title.localeCompare(b.data.title),
);
for (const { id, data } of entries) {
const route = id.endsWith('/index') ? id.slice(0, -'/index'.length) : id;
lines.push(`- [${data.title}](${origin}${base}${route}/): ${data.description}`);
}
lines.push('');
}

lines.push('## Other versions and languages', '');
if (base === '/') lines.push(`- [Next version](${origin}/next/llms.txt): preview docs for the next Echo release.`);
else lines.push(`- [Stable version](${origin}/llms.txt): current release docs.`);
for (const [locale, label] of [
['es', 'Español'],
['ja', '日本語'],
['pt-br', 'Português'],
['zh-cn', '简体中文'],
]) {
lines.push(`- [${label}](${origin}${base}${locale}/): localized documentation.`);
}

return new Response(`${lines.join('\n')}\n`, {
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
});
};
Loading