3 điểm bởi GN⁺ 2023-09-11 | 1 bình luận | Chia sẻ qua WhatsApp
  • Trang web tĩnh dễ lưu trữ, nhanh và ít cần bảo trì, nên với một trang đơn giản thì chỉ cần vài dòng Makefile mà không phải học thêm trình dựng riêng
  • Cấu trúc cốt lõi là giữ nguyên cách sắp xếp tệp trong source, nối thêm header.html vào các tệp HTML, còn các tệp khác thì sao chép nguyên trạng sang build
  • make build dùng find source -type fpatsubst để ánh xạ tệp đầu vào/đầu ra, rồi áp dụng các quy tắc make khác nhau cho .html và các tệp còn lại
  • Có thể thêm các target bổ sung khi cần cho việc làm nổi bật trang hiện tại, chuyển đổi Markdown, máy chủ cục bộ, tự động build lại khi có thay đổi và triển khai GitHub Pages
  • Ít phụ thuộc và chỗ cần chỉnh sửa rất rõ ràng, nên với các trang có yêu cầu nhỏ thì luồng build tự làm có thể nhanh hơn trình tạo tổng quát

Trang web tĩnh cơ bản chỉ với Makefile

  • Trình tạo trang web tĩnh cho ra kết quả dễ host, nhanh và gánh nặng bảo trì rất nhỏ
  • Với một trang đơn giản, tự viết script có thể nhanh hơn và thỏa mãn hơn so với học rồi tùy biến một trình dựng trang có sẵn
  • Nếu là một trang web thông thường không có timestamp tự cập nhật hay RSS feed, thì cấu hình còn có thể đơn giản hơn cả script dành cho blog
  • Yêu cầu cơ bản

    • Mọi tệp đầu vào đều đặt trong thư mục source, và đầu ra cũng giữ nguyên cấu trúc thư mục đó
    • Thêm header.html vào đầu mọi tệp HTML
    • Các tệp không phải HTML được sao chép nguyên vẹn vào thư mục build
  • Quy tắc Makefile

    • Target build có các phụ thuộc ánh xạ mọi tệp bên dưới source sang tệp đầu ra tương ứng bên dưới build
    • Bản thân nó không làm việc gì, nhưng đóng vai trò điểm vào để các quy tắc phù hợp với từng tệp được áp dụng
    • Quy tắc build/%.html phụ thuộc vào source/%.html, header.html, và Makefile
    • Dùng mkdir -p $(dir $@) để tạo thư mục đầu ra, rồi cat header.html $< > $@ để ghép phần header và HTML đầu vào thành đầu ra
    • Quy tắc build/% sao chép các tệp không phải HTML bằng cp $< $@ nguyên trạng
    • Chỉ với header.html và các quy tắc này, chạy make build sẽ tạo ra thư mục build có thể mở cục bộ hoặc đưa lên máy chủ web

Ví dụ mở rộng và các target phụ trợ

  • Hiển thị trang hiện tại

    • Nếu làm nổi bật trang hiện tại trong điều hướng, người truy cập có thể nhìn thấy ngay vị trí của mình trong trang web
    • Trong ví dụ, liên kết điều hướng được tìm ra và thêm class current
    • sed -E 's|(href="$(subst source,,$<))|class="current" \\1|' header.html | cat - $< > $@
    • Cách thay thế thực tế cần thay đổi theo cấu trúc markup đang dùng
  • Tạo HTML từ Markdown

    • Nếu không muốn viết HTML trực tiếp hoặc nội dung sẵn có ở dạng Markdown, có thể nối thêm một bộ chuyển đổi Markdown-to-HTML bằng pipe
    • Ví dụ dùng smu
    • smu $< | cat header.html - > $@
    • Quy tắc cơ bản vẫn giả định build/foo.html được tạo từ source/foo.html
    • Với tệp Markdown, cần либо giữ hậu tố .html, либо sửa quy tắc để tìm tệp .md làm đầu vào
  • Xem trước cục bộ

    • Một số trang khó xem trước chính xác nếu mở trực tiếp tệp cục bộ trong trình duyệt, lý do phổ biến là dùng liên kết tuyệt đối thay vì liên kết tương đối
    • Python đã được cài sẵn trên nhiều hệ thống và có kèm máy chủ web phù hợp cho mục đích này
    • Target serve: python -m http.server -d build
  • Tự động build lại khi có thay đổi

    • Trong lúc làm trang, việc mỗi lần đều phải build lại thủ công là khá phiền
    • Dùng entr thì có thể tự động chạy make build khi source, header.html, hoặc Makefile thay đổi
    • Target watch: find source header.html Makefile | entr make build
    • Nếu muốn tránh thêm phụ thuộc, cũng có thể dùng inotifywait
  • Triển khai GitHub Pages

    • Nếu kho mã nằm trên GitHub, thì host HTML kết quả bằng GitHub Pages là một lựa chọn tự nhiên
    • Việc căn chỉnh lệnh triển khai để không phải bận tâm đến các chi tiết của git hơi rắc rối một chút, nhưng có thể xử lý bằng git worktree
    • Cách làm dựa trên bài viết của Sangsoo Nam
    • Thêm nhánh gh-pages làm worktree public_html
    • Sao chép build/* vào public_html
    • Chạy git add --all, git commit -m "Deploy to github pages", và git push origin gh-pages
    • Cuối cùng xóa worktree bằng git worktree remove public_html
  • Ví dụ thực tế

    • Có thể xem một trang được làm theo cách này tại karlb/astridbartel.de
    • Một trình tạo trang web tĩnh hoàn chỉnh có thể bắt đầu chỉ với sáu dòng Makefile, rồi nhanh chóng điều chỉnh theo nhu cầu mà không có phụ thuộc lạ hay gánh nặng bảo trì

1 bình luận

 
GN⁺ 2023-09-11
Ý kiến trên Hacker News
  • Trước đây tôi tạo website cá nhân (https://pablo.rauzy.name/) bằng một Makefile đơn giản
    Sau đó tôi thêm các tính năng như tin tức và RSS feed, danh sách tự động các bài nghiên cứu và tài liệu bài giảng, danh sách sách có thể lọc theo thẻ. Hiện giờ vẫn là Makefile, nhưng bản thân Makefile thậm chí đã đơn giản hơn một chút; thay vào đó nó gọi các script Bash dùng những tiện ích tuyệt vời xml2 và 2xml để xử lý HTML theo từng dòng. Chủ yếu tận dụng các tiện ích cốt lõi như grepsed
    Tôi cũng gắn thêm vài git hook để tự động gọi make khi cần, đặc biệt trên máy chủ từ xa nơi website được host, mỗi khi push vào repository thì phiên bản công khai sẽ được build lại
    Nó đã chạy rất tốt suốt nhiều năm, và lịch sử git thì quay ngược đến tận năm 2009. Nhìn các commit đầu tiên thì có beccad7 (FIRST_VERSION) Initial commit, d1cc6d7 adding link to Google Reader shared items, 6ccfd0c fix typo, d337959 adding link to Identi.ca account; đúng là đã 15 năm trôi qua
  • Vấn đề của cách này là nếu xóa file trong source/ thì nó không bị xóa trong build/
    Trong các dự án của tôi, build lại toàn bộ site vẫn đủ nhanh, nên tôi chọn cách xóa toàn bộ thư mục build trước khi build lại
    https://github.com/jez/jez.github.io/blob/source/Makefile#L1...
    Làm vậy vô hiệu hóa phần lớn incremental build, vốn là một lý do lớn để dùng build system, nhưng nếu biết trang nào muốn tạo lại thì vẫn có thể trực tiếp make riêng file đó
    Tôi muốn biết liệu trong Makefile có cách workaround phổ biến nào cho mẫu này không
    • Không chắc có phải mẫu phổ biến không, nhưng giải pháp của tôi là mỗi lần đều chạy một lệnh xóa các file “ngoài dự kiến”
      Dùng hàm shell của GNU Make để liệt kê file, rồi dùng hàm filter-out để lọc bỏ các đầu ra “dự kiến”. Đây là một hack xấu xí, nhưng đảm bảo lệnh được chạy mỗi lần bằng cách khiến nó chạy như một phần của việc mở rộng biến thông qua hàm shell
      Link Makefile: https://github.com/jaredkrinke/make-blog/blob/main/Makefile
    • Xóa file và đổi tên là vấn đề phổ biến trong nhiều hệ thống quản lý phiên bản/build system
      Ngoài lựa chọn hạt nhân như make clean, có thể tạo các target Make riêng cho việc xóa và đổi tên. Ví dụ, để make rm sourcefile hoặc make mv sourcefile newsourcefile xử lý đồng thời việc xóa/đổi tên cả nguồn lẫn các target đã sinh ra
      Thực tế, ngay cả với các blog hay dự án online khá lớn, chu trình make clean / make all thường vẫn đủ nhanh, và hay cần khi sửa template hoặc các yếu tố thiết kế site. Nếu quy mô lớn đến mức phải lo thời gian rebuild, có lẽ nên dùng một CMS thực sự, quản lý nguồn trong database và sinh động khi client truy cập
    • make clean là được mà?
    • Làm kiểu này có vẻ được
      rm/%.html:
      @rm -f source/%.html build/%.html
      Chạy bằng $ make rm/page.html
    • Build system đa dụng Shake có tính năng prune đúng cho mục đích này
      http://neilmitchell.blogspot.com/2015/04/cleaning-stale-file...
      Nhưng tôi nghĩ giải pháp tốt nhất vẫn dùng được với make là tạo target make dist để tạo archive .tar.gz cuối cùng của output. Nếu viết rule đúng, các file cũ sẽ không bị đưa vào. Với dự án lớn có thể chậm, nhưng trong lúc phát triển dù sao cũng không cần dùng, chỉ dùng khi release. Incremental build vẫn khả dụng, chỉ có file .tar.gz cuối cùng là được tạo lại từ đầu
  • Tôi được truyền cảm hứng trực tiếp từ script shell blog.sh của Karl được nhắc trong bài này
    Tôi lấy nó về chỉnh sửa và tạo ra barf, trình tạo site tĩnh tối giản của mình. Nó đã không tồn tại nếu Karl không công bố công việc tuyệt vời đó
    [0]: https://github.com/karlb/karl.berlin/blob/master/blog.sh
    [1]: https://barf.bt.ht
    • Có vẻ cùng gu đấy. Cái của tôi gọi là shite, và tôi dùng nó để tạo site của mình. Tên gọi ám chỉ chất lượng phần mềm :)
      Điều tôi thích nhất là đến giờ chưa phải nâng cấp gì cả, và có vẻ sau này cũng sẽ không cần. Điều hay tiếp theo là có hot reload mà không cần JavaScript
      [1] https://github.com/adityaathalye/shite
      [2] https://evalapply.org
  • Trộn thêm một chút m4 thì vẫn giữ được cùng cách tiếp cận chỉ có khung xương đó, đồng thời có thêm chút linh hoạt

Tôi từng duy trì một website nhỏ được làm theo kiểu đó khoảng 20 năm trước. Nhưng ngoài website cá nhân ra, tôi không chắc mô hình này ngày nay còn phù hợp hay không. Vì cách này về bản chất buộc mọi thứ quay về vai trò của Web 1.0. Mọi người dùng đóng góp đều phải thành thạo HTML, hoặc ai đó phải gánh lấy việc lặt vặt gọi là “webmaster”
[1] https://en.wikipedia.org/wiki/M4_(computer_language)

  • Không có chuyện “một chút m4” đâu
    Bạn bắt đầu một dự án sạch sẽ và tự hứa lần này sẽ không đụng đến m4. Rồi để giảm mã lặp, bạn thêm một lời gọi m4 nhỏ
    Một năm sau, bạn lần qua 5 lớp mở rộng macro để tìm lý do vì sao từ “cat” âm thầm biến mất khỏi website, và cuối cùng phát hiện một lập trình viên junior đã tự triển khai vòng lặp for thay vì chép từ manual, rồi làm hỏng dấu nháy
    Sau khi giải quyết vấn đề trước mắt, bạn kết luận rằng debug DSL quá khó, nên lôi vào tệp macro M4 vẫn được chép từ dự án này sang dự án khác. Sau đó bạn mất cả ngày thay toàn bộ chỗ dùng define bằng các macro tạo macro, để thêm chú thích vào output nhằm làm cho script tạo stack trace hoạt động
    Ở dự án tiếp theo, bạn sẽ đặt ra luật tuyệt đối. Cấm m4! Tất nhiên có thể ngoại lệ ở đúng một chỗ đó
  • Thay vì dựa vào thay thế văn bản chung chung như m4 hay perl, tôi khuyên nên dùng SGML, nền tảng và siêu tập chung của HTML và XML
    SGML cung cấp sẵn macro văn bản có kiểm tra kiểu đơn giản, tức mở rộng entity, và cũng có thể mở rộng macro có tham số hóa biết kiểu. Ở đây kiểu là kiểu nội dung tổng quát của phần tử markup, tức các phần tử con được phép và thứ tự của chúng, đồng thời cũng tính đến việc mở rộng hoặc escape theo các ngữ cảnh như thuộc tính, CDATA, RCDATA
    Việc mở rộng và escape đúng cách các bình luận người dùng có khả năng độc hại, đồng thời áp dụng các quy tắc do người dùng đặt ra, chẳng hạn cho phép markup cấp inline nhưng cấm phần tử script, là việc chỉ SGML mới có thể mở rộng đúng. Cũng có thể mở rộng Markdown hoặc cú pháp wiki thành HTML, đưa nội dung HTML bên ngoài/syndication vào, tạo RSS và outline cho điều hướng, v.v. Nó rất hợp với các tác vụ chuẩn bị site tĩnh khá phức tạp trên dòng lệnh
    [1]: https://sgmljs.net/docs/producing-html-tutorial/producing-ht...
    [2]: https://sgmljs.net/docs/sgmlproc-manual.html
  • Bạn nên thử dùng envsubst thay vì tìm-thay thế bằng m4 hay sed
    Đây là chương trình thay thế các tham chiếu biến kiểu Bash, ví dụ $TITLE, bằng giá trị biến môi trường
    export CURRENT="..."
    cat page.html | envsubt
  • “Một chút m4” ư, làm ơn tuyệt đối đừng
    m4 cũng chẳng hay ho gì ngay cả với tư cách một ngôn ngữ lập trình khó hiểu
  • Tôi đã thử một lần, và sẽ không bao giờ làm lại
    Chỉ riêng việc nó từng chạy trong sendmail không đủ để biện minh cho bất cứ thứ gì
  • Tôi thích việc gần như mọi blog của lập trình viên bắt gặp trên HN đều có RSS feed
    Mỗi khi đọc được một bài thú vị ở đây, tôi lại đăng ký feed đó. Dù là site Wordpress, Bear Blog, Micro.blog, blog Havenweb hay feed từ site tự làm, tôi đều thêm vào module Really Social Sites của Hey Homepage
    Cuối cùng tôi muốn công khai danh sách blog này, giống như Kagi đang làm với sáng kiến Small Web. Tuy nhiên, để tăng chất lượng thì curate là then chốt, và khi nghĩ đến curate, việc bắt đầu một kiểu tạp chí online nào đó trông có vẻ tự nhiên
    • Tôi đang cố hiểu liệu với tư cách lập trình viên, việc tôi không muốn có blog riêng có kỳ lạ không
      Mọi người lấy đâu ra cảm giác “có quyền” theo nghĩa tích cực rằng có thể chia sẻ với người khác? Tôi tò mò vì sao họ giả định người khác sẽ quan tâm đến thứ mình đang làm. Đôi khi nó giống như một cuộc cạnh tranh. Kiểu như “để có like hoặc lượt xem trên blog, mình phải làm thứ ngầu nhất có thể”
      Hợp tác dĩ nhiên là tốt, và chỉ khả thi khi mọi thứ được công khai. Nhưng tôi không rõ ranh giới giữa “làm vì cái này trông hay” và “cố chia sẻ với người khác để nhận phản hồi” nằm ở đâu
    • Cũng có https://prose.sh tương tự Bear Blog
  • Một người bạn từng giải thích cách tạo bài báo khoa học bằng make
    Anh ấy nói chỉ cần thay đổi một tệp test, bằng một lệnh duy nhất có thể tạo lại toàn bộ bài báo, bao gồm chạy test và tạo biểu đồ
  • Ý tưởng thì gọn gàng, nhưng nếu đã push lên GitHub rồi, bạn có thể chỉ đưa source lên và GitHub sẽ xuất bản Markdown thành trang được host cho bạn
    https://pages.github.com/
    • Như vậy bạn sẽ phụ thuộc vào GitHub nhiều hơn là chỉ hosting đơn thuần. Tốt hơn là ngay từ đầu nên để việc tạo site có thể chạy cục bộ
  • Tôi thích đoạn mã này
    Cái của tôi hơi over-engineered vì muốn hot reload không dùng JavaScript, và đó là một màn yak shaving thú vị
    Ý tưởng cơ bản thì giống nhau. Dùng heredoc cho template, dùng compiler để biến văn bản thuần thành HTML, trường hợp của tôi là pandoc. Tôi dùng CSV trung gian để tạo chỉ mục, và cũng có một kỹ thuật sed hữu ích để trích front matter. Cổ điển mà hay
    [1] https://github.com/karlb/karl.berlin/blob/master/blog.sh
    [2] https://github.com/adityaathalye/shite
    [3] Cách tôi làm: https://github.com/adityaathalye/shite/blob/master/bin/templ...
    • Cách tiếp cận GEMINI của anh ấy khá buồn cười. Dùng regex để lột bỏ phần lớn định dạng

Tuy nhiên có một vài hạn chế. Tôi sắp xếp bài viết theo namespace và đưa ngày vào URL, nhưng make không xử lý trực tiếp việc đó tốt lắm

  • Nhìn vào loại script này sẽ thấy chính xác vì sao chúng ta dùng những thứ như esbuild hay vite thay vì tự làm
  • Ưu điểm của make là khi có thay đổi nhỏ trong một chương trình lớn được build bằng trình biên dịch chậm, nó giúp build lại tăng dần nhanh hơn rất nhiều
    Một tác vụ build lại toàn bộ mất 40 phút có thể hoàn tất trong khoảng 3 giây
    Nếu một site tĩnh chỉ cần cat header chung và vài trăm file HTML là có thể được tạo từ đầu trong chưa đến 1 giây, thì không có lợi ích gì khi dùng make thay cho script. Chỉ làm phát sinh nguy cơ thực hiện một bản build không hoàn chỉnh do lỗi phụ thuộc
    • Nếu phụ thuộc file thực sự không quan trọng, có thể đánh dấu target build là .phony
      Dù vậy vẫn có thể có các phân tách như make buildmake push
  • Chà, cái này gần như đúng y những gì tôi định làm cho site của mình
    Ở các dự án nhỏ khác, tôi đã dùng một shell script rất nhỏ như một bundler tạm thời. Cách làm là chèn CSS và JS vào trong HTML, với mục tiêu là vẫn có thể phục vụ các file chưa build ở local