mirror of
https://github.com/zitadel/zitadel.git
synced 2026-07-25 18:28:00 +00:00
# Which Problems Are Solved Pages in `apps/docs` that embed source from GitHub via the Docusaurus convention ```` ```js reference https://github.com/zitadel/actions/blob/main/examples/org_metadata_claim.js ``` ```` stopped working after the migration from Docusaurus to fumadocs. The old `docusaurus-theme-github-codeblock` plugin used to fetch the file and render it; fumadocs has no support for that meta, so the page rendered the raw URL as plain code-block text. Visible at `/docs/apis/actions/code-examples` and 16 other pages. # How the Problems Are Solved - Converted every ```` ```<lang> reference\n<URL>\n``` ```` block (46 total across 17 `.mdx` files) to the native fumadocs JSX form: `<GithubCodeBlock url="<URL>" />`. The existing `<details>`/`<summary>` collapsibles around blocks are kept — they're an authoring choice, not part of the rendering bug. - Updated `apps/docs/components/github-code-block.tsx` to render via `DynamicCodeBlock` from `fumadocs-ui/components/dynamic-codeblock` (proper shiki highlighting) instead of raw `CodeBlock` + `Pre` (which produced unhighlighted output). Also fixed language detection so a URL hash like `#L10-L20` no longer pollutes the language token. - Registered `GithubCodeBlock` globally in `apps/docs/mdx-components.tsx`, matching how every other shared component (`APIPage`, `Callout`, `Tab/Tabs`, `Step/Steps`, `Admonition`, `TerminologyUpdate`) is exposed. MDX files no longer need a local `import`. # Additional Changes - Normalized the two MDX files that were already using the JSX form (`examples/secure-api/python-django.mdx`, `examples/secure-api/java-spring.mdx`): removed their now-redundant local `import { GithubCodeBlock }` and rewrote 9 long-form `<GithubCodeBlock url="..."></GithubCodeBlock>` tags to self-closing for consistency. # Additional Context Verified locally with `pnpm --filter @zitadel/docs dev`: - `/docs/apis/actions/code-examples` — 20 shiki-highlighted code blocks rendered inside the `<details>` collapsibles (was 0). - `/docs/apis/openidoauth/claims` — line-range hashes (`#L9-L11`) honored. - `/docs/examples/login/flutter` — mixed languages (xml/dart/html) detected and highlighted. - `/docs/guides/integrate/external-audit-log` — edge case of fenced reference indented inside a numbered list also converted and rendered. Greps: - `^[ \t]*\`\`\`[a-zA-Z0-9]+ reference` in `apps/docs/content/**/*.mdx` → 0 matches. - `<GithubCodeBlock url="` in `apps/docs/content/**/*.mdx` → 55 matches. - `from '@/components/github-code-block'` in `apps/docs/content/**/*.mdx` → 0 matches. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
54 lines
2.1 KiB
TypeScript
54 lines
2.1 KiB
TypeScript
import { DynamicCodeBlock } from 'fumadocs-ui/components/dynamic-codeblock';
|
|
|
|
const REVALIDATE_SECONDS = 60 * 60 * 24;
|
|
|
|
function toRawGithubUrl(url: string): string {
|
|
const parsed = new URL(url);
|
|
if (parsed.protocol !== 'https:' || parsed.hostname !== 'github.com') {
|
|
throw new Error(`GithubCodeBlock only accepts https://github.com URLs, got: ${url}`);
|
|
}
|
|
const segments = parsed.pathname.split('/').filter(Boolean);
|
|
if (segments.length < 5 || segments[2] !== 'blob') {
|
|
throw new Error(`Unexpected GitHub URL shape, expected /<owner>/<repo>/blob/<ref>/<path>, got: ${url}`);
|
|
}
|
|
const [owner, repo, , ref, ...path] = segments;
|
|
return `https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${path.join('/')}`;
|
|
}
|
|
|
|
function parseLineRange(hash: string): { start: number; end: number } | null {
|
|
const match = hash.match(/L(\d+)(?:-L(\d+))?/);
|
|
if (!match) return null;
|
|
const start = parseInt(match[1], 10);
|
|
const end = match[2] ? parseInt(match[2], 10) : start;
|
|
return { start, end };
|
|
}
|
|
|
|
function sliceAndDedent(code: string, range: { start: number; end: number }): string {
|
|
const selected = code.split('\n').slice(range.start - 1, range.end);
|
|
const minIndent = selected.reduce((min, line) => {
|
|
if (line.trim().length === 0) return min;
|
|
const indent = line.match(/^\s*/)?.[0].length ?? 0;
|
|
return Math.min(min, indent);
|
|
}, Infinity);
|
|
const dedent = minIndent === Infinity ? 0 : minIndent;
|
|
return selected.map(line => line.slice(dedent)).join('\n');
|
|
}
|
|
|
|
export async function GithubCodeBlock({ url }: { url: string }) {
|
|
const rawUrl = toRawGithubUrl(url);
|
|
const response = await fetch(rawUrl, { next: { revalidate: REVALIDATE_SECONDS } });
|
|
if (!response.ok) {
|
|
throw new Error(`GithubCodeBlock failed to fetch ${rawUrl}: ${response.status} ${response.statusText}`);
|
|
}
|
|
let code = await response.text();
|
|
|
|
const range = parseLineRange(new URL(url).hash);
|
|
if (range) {
|
|
code = sliceAndDedent(code, range);
|
|
}
|
|
|
|
const lang = new URL(url).pathname.split('.').pop() || 'text';
|
|
|
|
return <DynamicCodeBlock lang={lang} code={code} />;
|
|
}
|