Kalian akan memodifikasi node HAST seperti figure dan pre setelah rehype-pretty-code bekerja, membuat rehype plugin kustom, serta menyusun rangkaian plugin dengan urutan yang benar agar transformasi tidak saling merusak.

rehype-pretty-code menghasilkan markup yang sudah lengkap, tetapi kebutuhan nyata sering melampaui fitur bawaannya: menambahkan bahasa pada tombol copy, mengubah class tertentu, atau menyuntikkan metadata dari luar. Semua itu bisa dilakukan dengan memodifikasi pohon HAST setelah plugin selesai.
Episode 17 membahas custom HAST dan rehype plugin lanjutan: memahami bentuk node HAST, menulis plugin rehype kustom yang mengunjungi dan mengubah node, serta menyusun urutan plugin yang benar dalam pipeline yang kompleks.
HAST adalah representasi pohon untuk HTML yang dipakai pipeline rehype. Elemen seperti figure, pre, dan code adalah node element dengan properti tagName, properties, dan children. Atribut seperti data-rehype-pretty-code-figure tersimpan di properties sebagai pasangan kunci dan nilai.
Berikut bentuk salah satu node hasil rehype-pretty-code:
{
type: "element",
tagName: "figure",
properties: { dataRehypePrettyCodeFigure: "" },
children: [{ type: "element", tagName: "pre", children: [] }]
}Pemahaman struktur ini menjadi dasar untuk menulis plugin yang menargetkan elemen tertentu. Semua modifikasi kalian berakhir sebagai atribut pada HTML akhir.
Modifikasi pada pohon lebih aman daripada memanipulasi string HTML. Karakter yang perlu di-escape tetap ditangani oleh rehype-stringify, dan atribut yang ditambahkan melalui properties diubah menjadi atribut HTML yang valid. Ini menjaga output tetap bersih dan terhindar dari bug escape.
Rehype plugin adalah fungsi yang menerima options dan mengembalikan transformer. Transformer menerima pohon HAST dan boleh mengubahnya secara langsung:
import { visit } from "unist-util-visit";
export function rehypeAddLangAttribute(options) {
return (tree) => {
visit(tree, { tagName: "figure" }, (node) => {
const code = node.children.find(
(child) => child.tagName === "code",
);
const lang = code?.properties?.dataLanguage;
if (lang) {
node.properties.dataLang = lang;
}
});
};
}Plugin di atas memakai unist-util-visit untuk menemukan semua figure. Setiap figure yang berisi code dengan bahasa mendapat atribut data-lang yang bisa dibaca JavaScript di sisi klien.
Daftarkan plugin setelah rehype-pretty-code agar node sudah berbentuk figure saat diakses:
unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypePrettyCode, { theme: "github-dark-default" })
.use(rehypeAddLangAttribute)
.use(rehypeStringify);Urutan ini menjamin plugin kustom melihat struktur yang sudah lengkap. Jika dipasang sebelum rehype-pretty-code, tidak ada node figure yang ditemukan dan plugin tidak melakukan apa pun.
Terkadang kalian perlu menandai <pre> agar CSS bisa menargetkannya secara spesifik. Tambahkan atribut langsung ke properti node:
import { visit } from "unist-util-visit";
export function rehypeMarkCodeBlocks() {
return (tree) => {
visit(tree, { tagName: "pre" }, (node) => {
node.properties.dataCodeBlock = "";
});
};
}Setelah plugin ini berjalan, setiap <pre> membawa data-code-block. CSS dan JavaScript klien bisa memakainya tanpa harus mengandalkan struktur figure yang mungkin berbeda antar versi plugin.
Plugin juga bisa menambah elemen, misalnya membungkus kode dengan wrapper untuk styling. Gunakan node element biasa dan tempatkan pada children:
visit(tree, { tagName: "figure" }, (node) => {
node.children.push({
type: "element",
tagName: "div",
properties: { className: ["code-footer"] },
children: [],
});
});Node yang disisipkan harus mengikuti format HAST: type, tagName, properties, dan children. rehype-stringify akan mengubahnya menjadi HTML yang benar.
Pipeline produksi sering menggabungkan banyak plugin. Contoh urutan yang masuk akal:
unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypeRaw)
.use(rehypePrettyCode, { theme: "github-dark-default" })
.use(rehypeSlug)
.use(rehypeAutolinkHeadings)
.use(rehypeSanitize)
.use(rehypeStringify);rehypeRaw mengubah HTML mentah di dalam Markdown menjadi node, lalu rehypePrettyCode memproses code block, diikuti rehypeSlug dan rehypeAutolinkHeadings untuk heading, dan sanitasi di akhir.
Prinsipnya sederhana: plugin yang mengubah struktur harus dijalankan sebelum plugin yang membaca struktur tersebut. Bahasa di-highlight sebelum heading diberi id, dan sanitasi selalu di akhir untuk membersihkan semua output sebelumnya. Menukar urutan bisa membuat plugin kehilangan target atau membiarkan markup yang tidak aman lolos.
Inti yang harus dibawa pulang:
tagName, properties, dan children.unist-util-visit membantu menemukan node tertentu dalam pohon.Di episode 18 kalian akan belajar Shiki core dan highlighter API: memakai createHighlighter, createHighlighterCore, codeToHtml, codeToTokens, dan getSingletonHighlighter, serta memakai Shiki di luar rehype seperti markdown-it, CLI, atau render manual.