Menu docs

Fitur & Build Notes · · 3 menit baca

Membangun docs engine halaman ini: registry + markdown, tanpa framework

Dokumentasi yang mendokumentasikan dirinya: markdown sebagai text module, satu registry untuk sidebar/sitemap/hreflang, ToC via injeksi anchor regex, dan contoh live dari komponen asli — tanpa Docusaurus.

TypeScriptmarkdown-itHono JSXCloudflare Workers

Halaman Docs yang sedang kamu baca ini dibangun tanpa framework dokumentasi — tanpa Docusaurus, tanpa VitePress. Total mesinnya: satu registry TypeScript, file markdown per bahasa, dan ±100 baris komponen JSX. Ini dokumentasi yang mendokumentasikan dirinya sendiri.

Kenapa tidak pakai framework docs?

Situs ini sudah punya server (Hono di Cloudflare Workers), design system (Tailwind), dan pipeline markdown (untuk blog). Framework docs berarti build step kedua, tema yang harus disamakan, dan deployment terpisah. Menumpang infrastruktur yang ada = fitur docs lengkap dengan biaya hampir nol.

1. Markdown sebagai modul, registry sebagai kebenaran

Wrangler bisa meng-import .md sebagai string (text module). Satu file registry mendeklarasikan semua docs — slug, kategori, judul dwibahasa, dan body per bahasa:

// src/lib/docs.ts
import idHtmx from "../../content/docs/id/form-kontak-htmx-workers.md";
import enHtmx from "../../content/docs/en/form-kontak-htmx-workers.md";

export const docs: Doc[] = [
  {
    slug: "form-kontak-htmx-cloudflare-workers",
    category: "features",
    navLabel: { id: "Form kontak HTMX", en: "HTMX contact form" },
    title: { id: "Membangun form kontak HTMX...", en: "Building an HTMX contact form..." },
    stack: ["HTMX", "Hono", "Cloudflare Workers"],
    body: { id: idHtmx, en: enHtmx },
    preview: { path: "/contact", label: { id: "Lihat form-nya live", en: "See it live" } },
  },
  // ...
];

Dari satu array ini, semuanya diturunkan otomatis: sidebar per kategori, halaman indeks, prev/next, sitemap, llms.txt, dan hreflang. Menambah dokumentasi = 2 file markdown + 1 entri.

2. ToC “Di halaman ini”: suntik anchor saat render

markdown-it tidak memberi id ke heading. Daripada menambah plugin, satu regex pass setelah render menyuntik id ke setiap <h2> sekaligus mengumpulkan daftar isi:

export function docHtml(doc: Doc, locale: Locale) {
  const toc: TocEntry[] = [];
  const html = renderMarkdown(pick(doc.body, locale)).replace(
    /<h2>([\s\S]*?)<\/h2>/g,
    (_m, inner: string) => {
      const id = anchorSlug(inner);
      toc.push({ id, text: stripDecode(inner) });
      return `<h2 id="${id}">${inner}</h2>`;
    },
  );
  return { html, toc };   // di-memo per isolate — markdown statis per deploy
}

Sidebar kanan tinggal me-render toc sebagai daftar link #anchor. Sticky positioning-nya murni CSS (sticky top-24).

3. Contoh live: komponen asli, bukan gambar

Docs komponen merender komponen sungguhan di halamannya. Registry hanya menyimpan kunci demo (data tetap serializable); pemetaan kunci → JSX hidup di komponen:

// src/components/docs.tsx
const demos: Record<string, () => unknown> = {
  "ui-components": () => (
    <>
      <BrowserFrame url="alifnugraha.my.id/preview">…</BrowserFrame>
      <SystemFlow nodes={["Storefront", "Checkout", "AI chat", "RAG", "Ops"]} />
    </>
  ),
};

{demo && <figure class="border border-line">{demo()}</figure>}

Kalau komponennya berubah, contoh di docs ikut berubah — dokumentasi yang mustahil basi.

4. Dwibahasa tanpa duplikasi struktur

Body markdown memang dua file (ID + EN), tapi struktur, meta, sidebar, dan schema hanya dideklarasikan sekali dengan tipe L = { id: string; en: string }. Route-nya menumpang mekanisme mirror situs (/docs/en/docs) — hreflang dan switcher bahasa dapat gratis.

Pelajaran

  • Untuk docs pribadi/project, registry + markdown mengalahkan framework docs: satu deployment, satu design system, nol build step tambahan.
  • Simpan data sebagai data (registry serializable); simpan render sebagai render (map JSX terpisah). Batas ini yang membuat keduanya mudah diubah.
  • Fitur “mewah” docs — ToC, prev/next, contoh live — masing-masing ternyata belasan baris, bukan alasan menambah dependensi.

Mau fitur seperti ini dibangun untuk bisnismu?