2
Arsitektur Aplikasi
FreeStuffDev adalah situs Astro yang dirender sepenuhnya pada fase build. astro.config.mjs hanya mendaftarkan tailwind() di dalam array integrations; tidak ada adapter dan tidak ada kunci output, sehingga konfigurasi tetap memakai mode statis bawaan astro.config.mjs:6-81. Konsekuensinya bersifat arsitektural: setiap pembacaan data terjadi saat astro build, dan tidak ada proses server yang menjalankan query ketika request datang.
Halaman ini memetakan tiga lapisan yang membentuk situs, yaitu content collection tools, routing berbasis berkas di src/pages, dan komponen tampilan di src/layouts serta src/components. Rincian schema dibahas di Skema Content Collection tools, rincian kerangka halaman dibahas di Layout, Sidebar, dan Tema, dan urutan perintah build dibahas di Build, Pemeriksaan Tipe, dan Deploy.
Peta tiga lapisan
graph TD
subgraph DATA["Lapisan data"]
cfg["src/content/config.ts<br/>defineCollection"]
coll[("content collection tools")]
end
subgraph ROUTE["Lapisan routing"]
idx["src/pages/index.astro"]
rec["src/pages/recent.astro"]
fav["src/pages/favorites.astro"]
tags["src/pages/tags/[slug].astro"]
alt["src/pages/alternative/[slug].astro"]
end
subgraph VIEW["Lapisan tampilan"]
layout["src/layouts/Layout.astro"]
sidebar["src/layouts/Sidebar.astro"]
card["src/components/Card.astro"]
alltags["src/components/AllTags.astro"]
end
cfg --> coll
idx -->|"getCollection('tools')"| coll
rec -->|"getCollection('tools')"| coll
fav -->|"getCollection('tools')"| coll
tags -->|"getStaticPaths"| coll
alt -->|"getStaticPaths"| coll
idx --> layout
idx --> card
layout --> sidebar
sidebar --> alltags
alltags -->|"getCollection('tools')"| coll
alt --> card
tags --> card
style cfg fill:#f59e0b,color:#000
style coll fill:#336791,color:#fff
style alltags fill:#8b5cf6,color:#fff
style idx fill:#2d7d46,color:#fff
%% fig: Peta tiga lapisan FreeStuffDev
%% desc: Collection tools menjadi satu-satunya sumber data, halaman di src/pages membacanya saat build, lalu merakit layout dan komponen; satu panah balik dari AllTags.astro memperlihatkan komponen yang juga membaca collection.Arah panah menunjukkan dependensi: lapisan routing bergantung pada lapisan data dan pada komponen tampilan, sedangkan lapisan tampilan hampir tidak pernah bergantung pada routing. Pengecualian tunggal, yaitu AllTags.astro, dibahas di bagian terakhir.
Lapisan data: content collection tools
Satu-satunya sumber data adalah collection tools. Kontraknya ditulis dengan defineCollection dan z.object di src/content/config.ts. Field title, snippet, link, tags, dan createdAt wajib; thumbnail, updatedAt, serta isPaid opsional src/content/config.ts:3-142. Collection itu diekspor dengan kunci 'tools' src/content/config.ts:16-183, dan nama kunci itulah yang dipakai setiap pemanggil getCollection('tools') di repository. Mengubah kunci atau field berarti mengubah kontrak seluruh pemanggil, bukan hanya satu halaman.
Lapisan routing: berkas di src/pages
Rute tidak dideklarasikan di satu berkas konfigurasi pusat; rute lahir dari nama berkas di src/pages. Berkas biasa menjadi rute tetap (index.astro, recent.astro, favorites.astro, submit.astro, about.astro). Berkas dengan tanda kurung siku menjadi rute dinamis dan wajib mengekspor getStaticPaths. src/pages/tags/[slug].astro mengambil seluruh tool, meratakan data.tags, membuang duplikat lewat new Set, lalu memakai setiap tag sebagai params.slug src/pages/tags/[slug].astro:7-21. src/pages/alternative/[slug].astro memakai tool.slug sebagai parameter src/pages/alternative/[slug].astro:8-20. Karena keduanya berjalan di fase build, jumlah halaman tag dan halaman alternatif ditentukan oleh isi collection pada saat build itu berjalan.
| Berkas routing | Jenis | Sumber data saat build |
|---|---|---|
src/pages/index.astro | rute tetap / | getCollection('tools', ...) dengan filter isPaid !== true src/pages/index.astro:9-114 |
src/pages/recent.astro | rute tetap /recent | getCollection('tools', ...) dengan filter yang sama |
src/pages/favorites.astro | rute tetap /favorites | getCollection('tools') tanpa filter |
src/pages/tags/[slug].astro | dinamis, getStaticPaths | seluruh collection, lalu tag unik |
src/pages/alternative/[slug].astro | dinamis, getStaticPaths | seluruh collection, lalu tool.slug |
Urutan dan penyaringan daftar per halaman dibahas di Halaman Recent dan Pengurutan, pencarian di sisi klien dibahas di Beranda dan Pencarian Klien, penelusuran tag di Indeks Tag dan Halaman Tag, dan halaman alternatif di Halaman Alternatif per Tool.
Lapisan tampilan: layout dan komponen
src/layouts/Layout.astro adalah satu-satunya kerangka HTML. Props di berkas itu berisi title, description, dan noIndex opsional src/layouts/Layout.astro:7-115; halaman favorit memakai noIndex={true} agar halaman itu tidak diindeks src/pages/favorites.astro:14-186. Layout juga memasang <ViewTransitions /> di dalam head src/layouts/Layout.astro:467 dan merender <Sidebar /> sebelum elemen <main> src/layouts/Layout.astro:92-938, sehingga navigasi sidebar ikut hadir di setiap halaman tanpa ditulis ulang.
Komponen bersifat daun dan hanya menerima props. Card.astro menerima tool dan isHidden lewat Astro.props src/components/Card.astro:39, dan menautkan setiap kartu ke /alternative/${tool.slug} sebagai string URL, bukan import modul src/components/Card.astro:6810. Halaman yang merakit lapisannya sendiri: src/pages/index.astro mengimpor Layout, Card, Header, dan Sponsors src/pages/index.astro:2-611, lalu memetakan hasil getCollection menjadi deretan <Card tool={tool} /> src/pages/index.astro:70-7412.
Satu panah balik: AllTags.astro
⚠️
src/components/AllTags.astromemanggilgetCollection('tools')tanpa filter src/components/AllTags.astro:2-413, padahal berkas itu berada di lapisan tampilan. Ia dirender olehSidebar.astrosrc/layouts/Sidebar.astro:3114, danSidebardirender olehLayout, jadi pembacaan collection penuh terjadi lagi untuk setiap halaman yang dirender. Karena panggilannya tanpa filterisPaid, daftar tag di sidebar juga mencakup tool berbayar, berbeda dari koleksi yang dipakai beranda src/pages/index.astro:9-114. Saat menambah collection atau mengubah filter, periksa berkas ini juga; kalau tidak, sidebar dan isi halaman memakai himpunan data yang berbeda. Pengolahan tag di dalamnya dijelaskan di Indeks Tag dan Halaman Tag.