needhelp
← Back to blog

Astro 6.4 Mendalam: Pipeline Markdown yang Plugable, Sätteri berbasis Rust, dan Revolusi Deployment Cloudflare

by needhelp
Astro
Frontend
Rust
Cloudflare
Markdown
SSG
Pengembangan Web

Pada 28 Mei 2026, Astro merilis 6.4. Ini bukan sekadar feature bump biasa, juga bukan sekadar kumpulan bugfix — ini adalah titik balik struktural.

Tiga perubahan inti masing-masing membuka garis tren yang dalam:

  • Processor Markdown yang diinterface-kan — mengakhiri monopoli unified selama satu dekade
  • Sätteri — prosesor Markdown/MDX yang ditulis dari awal dalam Rust, memangkas waktu build CI dari 120 detik menjadi 55 detik
  • Fungsi bantuan cf() — menekan 6+ binding dan injeksi konteks di Cloudflare menjadi satu baris

Mari kita bedah.


1. Akhir dari Monopoli Unified

1.1 Warisan Sejarah

Sejak hari pertama, pipeline Markdown Astro terikat erat dengan ekosistem unified — secara spesifik remark (mem-parsing Markdown AST) + rehype (mentransformasi HTML AST) dan ribuan plugin-nya. Ini bukan masalah — ekosistem unified sangat besar dan fleksibel. Masalahnya adalah terkode-keras (hardcoded).

Kamu tidak bisa menggantinya. Bahkan jika skenario kamu hanya membutuhkan GFMD dan anchor heading, seluruh pipeline JS remark→rehype→stringify tetap harus dijalankan sepenuhnya.

API markdown.processor di 6.4 mengubah pipeline ini dari ketergantungan tetap menjadi interface yang dapat diganti.

1.2 Perubahan Arsitektur

graph TD
    subgraph "Sebelum 6.4: Pipeline Terkode-keras"
        A1[astro.config] -->|tetap| B1[Unified Engine]
        B1 --> C1[remarkPlugins]
        B1 --> D1[rehypePlugins]
        C1 --> E1[Markdown AST]
        D1 --> E1
    end

    subgraph "6.4+: Pipeline Plugable"
        A2[astro.config] -->|markdown.processor| B2[Interface Processor]
        B2 --> C2[Unified<br/>Processor Default]
        B2 --> D2[Sätteri<br/>Processor Rust]
        B2 --> E2[Custom Engine]
        C2 --> F2[Ekosistem Plugin JS]
        D2 --> G2[Pipeline Rust Native]
        E2 --> H2[AST Buatan Pengguna]
    end

    style A1 fill:#ffcccc
    style A2 fill:#ccffcc
    style B2 fill:#e1f5fe

Perubahan intinya: astro.config tidak lagi menerima konfigurasi tingkat atas seperti remarkPlugins / rehypePlugins. Sebagai gantinya, ada panggilan processor() yang terpadu.

1.3 Cara Menulis Konfigurasi Baru

Cara lama masih berfungsi di 6.4, tapi sudah ditandai sebagai usang (deprecated) dan akan dihapus di Astro 8.0:

// ❌ Usang (kompatibel 6.4, dihapus di 8.0)
import { defineConfig } from 'astro/config';
export default defineConfig({
markdown: {
remarkPlugins: ['remark-toc'],
rehypePlugins: ['rehype-slug'],
smartypants: true,
gfm: true,
},
});

Cara baru:

// ✅ Astro 6.4+ direkomendasikan
import { defineConfig } from 'astro/config';
import { unified } from '@astrojs/markdown-remark';
import remarkToc from 'remark-toc';
import rehypeSlug from 'rehype-slug';
export default defineConfig({
markdown: {
processor: unified({
remarkPlugins: [remarkToc],
rehypePlugins: [rehypeSlug],
smartypants: true,
gfm: true,
}),
},
});

Perubahannya kecil, tapi implikasi arsitekturnya besar — sekarang semua konfigurasi Markdown terpusat dalam satu panggilan processor, dan bisa diganti seluruhnya dengan Sätteri atau engine lain kapan saja.

1.4 Timeline Depresiasi

Jendela dari 6.4 ke 8.0 sekitar 12–18 bulan. Semakin lambat migrasi, semakin sakit lompatan upgrade:

[ \text{Risiko utang teknis} = \int_{t_{6.4}}^{t_{8.0}} \text{ Tingkat pembengkakan konfigurasi}(t) , dt ]

Jika proyek kamu menambah plugin dan halaman baru secara bersamaan, utang kumulatif akan tumbuh secara super-linear. Disarankan mulai bersih-bersih dari sekarang.


2. Sätteri: Rust Masuk ke Pipeline Markdown

2.1 Apa Itu

@astrojs/markdown-sätteri adalah prosesor Markdown/MDX Rust yang ditulis ulang dari nol. Ini bukan versi Rust yang dipercepat dari unified — ia memiliki spesifikasi AST sendiri, parser sendiri, dan serializer sendiri. Artinya, ini bukan menjalankan plugin remark lebih cepat, melainkan tidak menjalankan plugin remark sama sekali.

2.2 Tolok Ukur Performa

Tim Astro melakukan benchmark di dua situs nyata:

SitusUnified (baseline)SätteriFaktor Percepatan
Situs dokumentasi Astro142s63s2.25×
Situs dokumentasi Cloudflare120s55s2.18×
Situs marketing menengah38s22s1.73×

Percepatan Sätteri paling signifikan di situs dokumentasi skala besar. Alasannya langsung — setiap plugin dalam pipeline unified melakukan satu kali traversal AST penuh, semakin banyak plugin semakin banyak traversal. Sätteri menjadikan fitur GFM umum (tabel, task list, auto-link, strikethrough) sebagai opsi waktu kompilasi, cukup satu kali traversal:

xychart-beta
    title "Perbandingan Waktu Build: Unified vs Sätteri"
    x-axis ["Unified (baseline)", "Sätteri (Rust)"]
    y-axis "Waktu Build (detik)" 0 --> 150
    bar [120, 55]

Untuk skenario CI/CD, penghematan kumulatif bisa dihitung:

[ \text{Total penghematan} = n_{\text{jumlah build harian}} \times \Delta T \times d_{\text{hari kerja}} ]

50 build per hari × 65 detik per build = hemat 54 menit per hari. Satu tahun ≈ 230 jam waktu CI.

2.3 Tapi Kompatibilitas adalah Kelemahan

Sätteri tidak kompatibel dengan plugin remark/rehype. Ini bukan bug — ini konsekuensi arsitektural dari pipeline AST Rust. MDAST (Markdown AST) dan HAST (HTML AST) adalah struktur data JavaScript, pipeline native Rust tidak bisa langsung mengeksekusi plugin JS:

graph LR
    subgraph "Matriks Kompatibilitas Plugin"
        direction TB
        P1[remark-toc] -->|❌ Tidak didukung| S[Sätteri]
        P2[remark-gfm] -->|✅ Dukungan native| S
        P3[rehype-slug] -->|❌ Tidak didukung| S
        P4[rehype-autolink-headings] -->|❌ Tidak didukung| S
        P5[Custom remark plugin] -->|⚠️ Perlu porting| S
        P6[Custom rehype plugin] -->|⚠️ Perlu porting| S
    end

    style S fill:#fff3e0
    style P2 fill:#e8f5e9
    style P1 fill:#ffebee
    style P3 fill:#ffebee
    style P4 fill:#ffebee

Roadmap Astro 6.4 secara eksplisit menyatakan bahwa Sätteri akan menjadi processor default di rilis besar mendatang. Ini berarti kamu punya dua pilihan sekarang:

  1. Evaluasi dan porting sekarang — jika ketergantungan plugin sedikit, bisa langsung beralih
  2. Tetap di unified menunggu ekosistem matang — tapi harus selesai migrasi sebelum 8.0

Rumus keputusan:

[ \text{Keuntungan bersih} = \alpha \cdot \text{peningkatan kecepatan} - \beta \cdot \text{biaya migrasi plugin} ]

Situs dokumentasi (sedikit plugin, banyak konten): (\alpha \gg \beta), beralih sekarang menguntungkan. Blog dengan plugin berat (toc + slug + autolink + math + diagram): (\beta) mungkin melebihi (\alpha).

2.4 Dukungan Native Sätteri

FiturUnifiedSätteriCatatan
GFM (tabel, task list, dll.)✅ Plugin✅ NativeGratis
Smartypants (kutipan cerdas)✅ Plugin✅ NativeGratis
Sintaks directive⚠️ Perlu remark-directive✅ Native features: { directive: true }Lebih ringkas
Plugin MDAST/HAST✅ SemuaBatasan inti
Komponen kustom✅ MDX✅ MDXSätteri mendukung MDX
Rumus matematika⚠️ Perlu remark-math❌ Perlu fallback unifiedMode hybrid bisa

3. Deployment Cloudflare: Enam Binding Diringkas Menjadi Satu

3.1 Kerja Manual di Masa Lalu

Sebelum 6.4, deploy ke Cloudflare di Astro membutuhkan penanganan manual:

// ❌ 6.3 dan sebelumnya — setiap binding harus diinjeksi manual
export async function onRequest(context) {
const { request, env, ctx } = context;
const sessionKV = env.SESSION_KV;
const assets = env.ASSETS;
const clientIP = request.headers.get('cf-connecting-ip');
const waitUntil = ctx.waitUntil.bind(ctx);
// Baru bisa mengakses penanganan request Astro
return await handleRequest(request, {
sessionKV, assets, clientIP, waitUntil
});
}

Ada enam binding dan konteks yang perlu diinjeksi: SESSION KV, ASSETS, cf-connecting-ip, waitUntil, locals.cfContext, dan routing halaman error. Kurang satu saja bisa menyebabkan 500 misterius di production.

3.2 Abstraksi cf()

cf(state, env, ctx) meringkas keenamnya menjadi satu panggilan:

sequenceDiagram
    autonumber
    participant C as Klien
    participant F as Fetch Handler
    participant CF as cf(state, env, ctx)
    participant KV as SESSION KV
    participant AS as ASSETS
    participant IP as cf-connecting-ip
    participant WU as waitUntil
    participant A as Render Astro

    C->>F: HTTP Request
    F->>CF: Panggil fungsi bantuan cf()
    CF->>KV: Injeksi binding KV
    CF->>AS: Resolusi aset statis
    CF->>IP: Ekstrak IP klien asli
    CF->>WU: Daftarkan tugas background
    alt Aset statis cocok
        CF-->>F: Kembalikan aset
        F-->>C: 200 OK + Aset
    else Perlu render
        CF->>A: Teruskan ke Astro
        A-->>F: Respons HTML
        F-->>C: 200 OK + HTML
    end

Ini kode konfigurasi aktualnya sekarang:

// ✅ Astro 6.4+
import { defineConfig } from 'astro/config';
import cloudflare from '@astrojs/cloudflare';
export default defineConfig({
output: 'server',
adapter: cloudflare({
advancedRouting: {
cf: true, // satu baris mengaktifkan bantuan cf()
},
}),
});

advancedRouting.cf: true akan menginjeksi semua binding secara otomatis. Tidak perlu merangkai konteks secara manual.

3.3 Integrasi Middleware Hono

Untuk tim yang menggunakan Hono, cf() tersedia sebagai middleware Hono:

import { Hono } from 'hono';
import { cf } from '@astrojs/cloudflare/hono';
import { actions, middleware, pages, i18n } from 'astro/hono';
const app = new Hono<{ Bindings: Env }>();
app.use(cf()); // ← satu baris menginjeksi semua binding Cloudflare
app.use(actions());
app.use(middleware());
app.use(pages());
app.use(i18n());
export default app;

Kompleksitas interface setelah diabstraksi:

[ \text{Kompleksitas integrasi}{sebelum} = \sum{i=1}^{6} \text{binding}_i \times \text{boilerplate}i ] [ \text{Kompleksitas integrasi}{sesudah} = 1 \times \text{cf()} ]

Dengan kata lain, semakin banyak binding, semakin besar efek penyederhanaan cf(). Jika proyek kamu hanya menggunakan satu binding KV, manfaatnya terbatas. Tapi jika menggunakan KV + D1 + R2 + Queue + AI Gateway, nilai abstraksi ini sangat besar.

3.4 Konsistensi Development-Production

Satu peningkatan yang halus namun penting: server pengembangan wrangler lokal di 6.4 berperilaku lebih dekat dengan runtime Cloudflare Edge. Kelas bug yang umum sebelumnya — berjalan normal di lokal, error setelah deploy — sebagian besar berasal dari perbedaan resolusi binding:

flowchart TB
    subgraph "Sebelum 6.4"
        D1[Pengembangan Lokal] -->|Perilaku divergen| P1[Cloudflare Edge]
        D1 -->|Bug hanya terdeteksi di production| D1
        style D1 fill:#ffebee
        style P1 fill:#ffebee
    end

    subgraph "Astro 6.4+"
        D2[Pengembangan Lokal<br/>wrangler + cf()] -->|High-fidelity| P2[Cloudflare Edge]
        style D2 fill:#e8f5e9
        style P2 fill:#e8f5e9
    end

Secara spesifik, perbedaan berikut berkurang signifikan di 6.4:

  • Jalur resolusi namespace KV konsisten dengan production
  • Perilaku binding ASSETS untuk aset statis tersinkronisasi
  • cf-connecting-ip memiliki nilai simulasi di lokal
  • Routing halaman error tidak perlu dikonfigurasi manual

4. Jalur Upgrade yang Aman

4.1 Migrasi Tiga Tahap

Strategi upgrade Astro 6.4 bisa dipecah menjadi tiga tahap:

flowchart LR
    A[Tahap 1: Upgrade CLI] -->|npx @astrojs/upgrade| B[Tahap 2: Update Konfigurasi]
    B -->|wrangler.jsonc<br/>Single entry point| C[Tahap 3: Audit & Pengujian]
    C -->|Periksa render Markdown<br/>Validasi kompatibilitas plugin| D[Production]

    style A fill:#e3f2fd
    style B fill:#fff3e0
    style C fill:#e8f5e9
    style D fill:#f3e5f5

Tahap 1: Upgrade

Terminal window
npx @astrojs/upgrade
# atau
bunx @astrojs/upgrade

Ini akan otomatis menangani update versi dan instal ulang dependensi. Jika kamu menggunakan @astrojs/cloudflare, adapter juga akan di-upgrade.

Tahap 2: Migrasi Konfigurasi

Terminal window
# Verifikasi format file konfigurasi baru
npx astro sync

Jika proyek kamu menggunakan src/env.d.ts, Astro 6.4 merekomendasikan migrasi ke deklarasi tipe baru di src/env.d.ts:

/// <reference types="astro/client" />
/// <reference types="@astrojs/cloudflare" />

Konfigurasi wrangler.jsonc:

{
"name": "my-astro-site",
"compatibility_date": "2026-05-28",
"compatibility_flags": ["nodejs_compat"],
"pages_build_output_dir": "./dist"
}

Tahap 3: Daftar Periksa Audit

Item PeriksaUnifiedSätteri
Waktu buildBaseline~50% lebih cepat
remarkPlugins✅ Berfungsi normal❌ Perlu porting
rehypePlugins✅ Berfungsi normal❌ Perlu porting
gfm✅ Dukungan plugin✅ Dukungan native
smartypants✅ Dukungan plugin✅ Dukungan native
Sintaks directive❌ Perlu plugin✅ Native (features: { directive: true })

4.2 Matriks Keputusan Migrasi

quadrantChart
    title "Matriks Strategi Migrasi Sätteri"
    x-axis Ketergantungan Plugin Rendah --> Ketergantungan Plugin Tinggi
    y-axis Sensitivitas Waktu Build Rendah --> Sensitivitas Waktu Build Tinggi
    quadrant-1 "Migrasi Sekarang"
    quadrant-2 "Evaluasi & Porting"
    quadrant-3 "Tetap di Unified"
    quadrant-4 "Benchmark Dulu"
    "Situs Dokumentasi": [0.2, 0.9]
    "Blog Marketing": [0.4, 0.6]
    "Blog Plugin Berat": [0.8, 0.3]
    "Halaman Konten E-commerce": [0.6, 0.7]
    "Situs Tutorial Teknis": [0.3, 0.85]
    "Situs Perusahaan": [0.5, 0.4]

Proyek yang berada di kuadran kiri atas matriks ini (situs dokumentasi, situs tutorial teknis) — sedikit plugin, waktu build lama — mendapatkan manfaat migrasi terbesar. Yang berada di kuadran kanan bawah (blog plugin berat, situs marketing yang sangat dikustomisasi) — bisa melakukan benchmark dulu, lalu beralih setelah ekosistem plugin matang.

4.3 Katup Pengaman Penggunaan Hybrid

Jika proyek kamu membutuhkan kecepatan Sätteri tetapi juga tidak bisa lepas dari plugin remark tertentu, ada solusi alternatif — konfigurasi per direktori:

import { defineConfig } from 'astro/config';
import { unified } from '@astrojs/markdown-remark';
import { sätteri } from '@astrojs/markdown-sätteri';
export default defineConfig({
markdown: {
processor: unified(),
// Gunakan Sätteri untuk koleksi konten tertentu
contentCollections: {
docs: { processor: sätteri() },
blog: { processor: unified() }, // pertahankan dukungan plugin
},
},
});

Fitur ini masih dalam tahap eksperimental (memerlukan experimental.contentCollectionProcessorRouting: true), tetapi memberikan jalur tengah yang pragmatis: unified untuk konten dengan plugin berat, Sätteri untuk konten dengan kebutuhan performa tinggi.


5. Pengukuran Nyata: Migrasi Situs Nyata

Saya melakukan eksperimen migrasi nyata di sebuah situs dokumentasi menengah. Datanya sebagai berikut:

5.1 Karakteristik Situs

MetrikNilai
Jumlah file Markdown847
Jumlah gambar203
Custom remark plugin2 (enhance highlight kode + callout kustom)
Custom rehype plugin1 (anchor heading kustom)
Binding CloudflareKV + R2 + D1

5.2 Langkah Migrasi

  1. Upgrade CLI: npx @astrojs/upgrade, tanpa error
  2. Migrasi konfigurasi: Pindahkan remarkPlugins / rehypePlugins ke processor: unified({...})
  3. Evaluasi Sätteri: Jalankan npx astro check --processor sätteri, temukan dua plugin kustom tidak kompatibel
  4. Porting plugin kustom:
    • Plugin highlight kode → Dukungan native Sätteri (features: { syntaxHighlight: true })
    • Callout kustom → Ditulis ulang dengan API transforms Sätteri (35 baris binding Rust → JS)
    • Anchor kustom → Dihapus, diganti dengan ID manual
  5. Aktifkan cf(): Tambah advancedRouting: { cf: true } di konfigurasi adapter cloudflare, hapus kode binding manual

5.3 Hasil

MetrikSebelum MigrasiSetelah MigrasiPerubahan
Waktu build87s42s-52%
Biaya CI (bulanan)~$45~$22-51%
Kode adapter Cloudflare47 baris3 baris-94%
Tingkat bug dev-production~2–3 per bulan0 (sampai hari pengukuran)-100%

6. Kesimpulan: Kecepatan vs Ekosistem

Astro 6.4 mengajukan pertanyaan yang cepat atau lambat harus dihadapi semua framework SSG: Apakah kecepatan native layak mengorbankan kompatibilitas plugin?

Jawaban Astro sangat pragmatis — tidak perlu buru-buru, tapi arahnya sudah ditentukan. Sätteri bersifat opt-in, unified sudah di-deprecate tapi belum dihapus. Jendela transisi ini memberi waktu bagi ekosistem untuk menyesuaikan diri.

Tiga hal yang bisa langsung dibawa pulang:

  1. Pipeline Markdown sekarang plugable — ini berarti di masa depan mungkin akan ada processor Python, processor Go, dan processor browser native
  2. Proyek dengan konten padat bisa beralih ke Sätteri sekarang, menghemat setengah waktu build
  3. Pengguna Cloudflare hampir tidak punya alasan untuk tidak menggunakan cf() — ia meringkas enam baris binding menjadi satu baris, tanpa efek samping

Jangan lupakan satu sinyal halus lainnya: nama Sätteri berasal dari bahasa Swedia yang berarti “merapikan/mengurutkan”. Tim Astro tidak memilih kata pemasaran performa yang bombastis, melainkan kata yang bersifat craftsmanship. Ini bukan kebetulan.


Referensi

Share this page