Viết blog với Gleam, Org-Mode và Pandoc: Quy trình tĩnh có kiểu dữ liệu
Một lập trình viên chia sẻ cách xây dựng blog cá nhân bằng Org-mode trong Emacs làm định dạng nguồn, Pandoc làm bộ chuyển đổi, cùng Gleam và Blogatto tạo pipeline tĩnh có kiểm tra kiểu dữ liệu. Toàn bộ môi trường được cố định bằng Nix để đảm bảo khả năng tái lập.
Viết blog với Gleam, Org-Mode và Pandoc: Khi lập trình viên mang cả Emacs vào quy trình xuất bản
Có những lập trình viên không chịu rời khỏi Emacs dù chỉ một bước. Bài viết mới trên blog Byzantine Systems kể lại hành trình xây dựng một trang web tĩnh bằng Org-mode làm định dạng nguồn, Pandoc làm bộ chuyển đổi, và Gleam kết hợp Blogatto làm pipeline có kiểm tra kiểu dữ liệu. Điểm đáng chú ý là mọi thứ đều được cố định bằng Nix để bất kỳ ai cũng có thể tái lập môi trường xây dựng chỉ với một lệnh.
Triết lý "ở lại Emacs" và cái gọi là bloat
Tác giả mở đầu bằng một trích dẫn từ Knuth năm 1984 về cảm giác phấn khích khi tìm ra một phương pháp lập trình mới. Với anh, cảm giác đó đến từ việc giữ toàn bộ quá trình viết trong Emacs — không phải vì lười, mà vì tính nhất quán.
"Emacs, khi nhìn từ góc độ triết lý UNIX, là bloat thuần túy. Nó không tuân theo nguyên tắc 'làm một việc thật tốt'. Nhưng chính sự thiếu vắng một môi trường thống nhất lại khiến userland trở nên bloat theo cách khác."
Tác giả lập luận rằng Emacs là "bloat tốt" — bởi nó gom mã nguồn, ghi chú, trích dẫn BibTeX và các đoạn tính toán vào cùng một chỗ. Nếu mỗi thứ cần một ứng dụng riêng, công việc viết lách sẽ biến thành một tour du lịch qua hàng loạt cửa sổ và TUI, nơi các mảnh ý tưởng bị phân tán.
Tại sao Org-mode thay vì Markdown?
Đây là câu hỏi mà nhiều người đặt ra. Markdown phổ biến, dễ đọc và được gần như mọi static-site generator hỗ trợ. Nhưng tác giả chỉ dùng Markdown như sản phẩm trung gian — do Pandoc tạo ra và Blogatto tiêu thụ — chứ không viết trực tiếp.
Lý do nằm ở sức mạnh của Org-mode với các source block có thể thực thi. Trong một tài liệu .org, bạn có thể chạy mã nguồn với tham số tùy chỉnh và lưu kết quả đầu ra ngay tại chỗ. Đó là lý do tác giả nhúng sơ đồ d2 vào ghi chú Org và tái tạo hình ảnh theo yêu cầu — một môi trường có thể tái lập, gần giống với Jupyter Notebook nhưng mang tính văn bản thuần túy hơn.
Nói cách khác, Markdown có thể bắt chước từng phần của Org-mode thông qua tiện ích mở rộng và công cụ ngoài, nhưng khi đó mô hình soạn thảo trở thành giao điểm ngẫu nhiên của một parser, một editor và một bộ plugin cụ thể. Còn các tính năng của Org-mode thuộc về một hệ thống nhất quán, và quan trọng hơn, chúng tương tác được ngay trong editor thay vì chỉ có ý nghĩa sau khi build.
Pipeline: Emacs, Pandoc, Blogatto và Lustre
Sơ đồ luồng xử lý từ Org-mode qua Pandoc đến Blogatto
Kiến trúc gồm nhiều công cụ, nhưng mỗi công cụ đảm nhiệm một việc hẹp:
- Org-mode giữ tài liệu nguồn phong phú, nơi tác giả vừa viết vừa tính toán.
- d2 tạo sơ đồ khai báo, giữ tinh thần "diagram as code".
- Pandoc chuyển đổi Org sang GitHub Flavored Markdown, kèm xử lý trích dẫn BibTeX.
- Gleam và Blogatto tạo pipeline tĩnh nhỏ gọn, có kiểm tra kiểu dữ liệu.
- Lustre định nghĩa HTML mà không cần thêm ngôn ngữ template riêng.
- Nix, flakes và devenv cố định môi trường xây dựng.
Nghe có vẻ quá mức cho một trang tĩnh đơn giản, nhưng tác giả để người đọc tự đánh giá qua ví dụ về trang liệt kê bài viết. Đó chỉ là một hàm Gleam trả về phần tử Lustre:
pub fn posts_page(posts: List(Post(msg))) -> Element(msg) {
let sorted = list.sort(posts, fn(a, b) { timestamp.compare(b.date, a.date) })
layout("Posts", [
html.h2([], [html.text("Posts")]),
case sorted {
[] -> html.p([], [html.text("No posts yet.")])
_ -> html.ul([], list.map(sorted, post_link))
},
])
}
Trang chỉ mục bài viết là mã Gleam bình thường, được kiểm tra kiểu dữ liệu — không phải một template đặc biệt nào đó.
Chuyển đổi bằng Pandoc và ba bộ lọc Lua nhỏ
Một hướng tiếp cận đầy cám dỗ là dạy Blogatto đọc trực tiếp Org. Tác giả cho biết sẽ khám phá điều này về lâu dài, thậm chí đóng góp vào repo chính. Nhưng hiện tại, Pandoc đã hiểu Org, đã xuất ra GitHub Flavored Markdown và đã có bộ xử lý trích dẫn, nên giai đoạn chuyển đổi có thể giữ mức nhàm chán — theo nghĩa tốt.
Lệnh thực thi trông như sau:
pandoc -s org/posts/example.org \
-t gfm \
--citeproc \
--bibliography=priv/bibtex/emacs.bib \
-o blog/posts/example/index.md
Ba bộ lọc Lua nhỏ xử lý những trường hợp mà chuyển đổi tổng quát không đủ:
verse.luagiữ các khối verse của Org thành HTML có cấu trúc.bibliography.luagiữ danh sách tham chiếu được nhóm đúng cách.images.luachuyển đường dẫn tệp hợp lý cạnh tài liệu Org sang đường dẫn hợp lý trên trang web đã xuất bản.
Điểm quan trọng: các tệp đã chuyển đổi là sản phẩm build và bị Git bỏ qua. Thư mục org/ là nguồn chân lý duy nhất.
Tái lập môi trường với Nix
Không có gì thay đổi ở đây — Nix vẫn là "băng dính" giữ mọi thứ hoạt động. Lệnh nix develop cung cấp Gleam, Pandoc, Make và các phụ thuộc build khác. Devenv làm cho shell trở nên dễ chịu khi dùng tương tác, trong khi cùng một flake tái lập môi trường xây dựng mà không phụ thuộc vào những gì tình cờ được cài đặt toàn cục.
Cấu hình còn xử lý một chi tiết tinh tế: gói fs (phụ thuộc bắc cầu từ Blogatto sang Filespy) xây dựng một NIF bằng plugin pc của rebar3, nên cần được đóng gói để các build thuần không phải truy cập hexpm.
Từ trong môi trường phát triển, toàn bộ quá trình build chỉ còn là:
make run
# hoặc
make dev
Kết luận: Nhiều công cụ, mỗi thứ một việc hẹp
Trang web này dùng một số lượng công cụ có phần phi lý cho một trang tĩnh đơn giản. Nhưng mỗi công cụ đều có vai trò rõ ràng: Org-mode giữ tài liệu nguồn phong phú, Pandoc rút gọn nó thành định dạng trao đổi di động, Blogatto biến định dạng đó thành trang web, Gleam và Lustre mang lại một dự án được kiểm tra kiểu dữ liệu tạo ra toàn bộ HTML tĩnh, và Nix đảm bảo khả năng tái lập.
Trung tâm của hệ thống vẫn là Emacs. Đó là quyết định có phần cố chấp của tác giả: giữ hành động viết, cấu trúc tài liệu và các tính toán hỗ trợ ở cùng một nơi. Với công việc đó, Org-mode vượt trội hơn Markdown vì nó là một mô hình tài liệu mà tác giả có thể tương tác trong lúc đang suy nghĩ.
Với độc giả Việt Nam quan tâm đến static site generator và quy trình viết lách kỹ thuật, đây là một ví dụ đáng tham khảo — không nhất thiết để sao chép nguyên xi, mà để thấy rằng một pipeline tưởng chừng phức tạp vẫn có thể rõ ràng, có kiểu dữ liệu và tái lập được nếu mỗi thành phần được chọn lọc kỹ càng.


