來自 shell 的小型熱重載靜態站點產生器。假設 Bash 4.4+。
警告:這裡有犛牛!
shite
的工作是幫助我製作我的網站:https://evalapply.org 因此, shite
的範圍、(錯誤)功能集、拋光將始終是生產級的,其中生產“可以在我的機器上運行” ) 」:)
目錄
嗯, shite
目標是製作網站。
它是一個由管線工作流程組成的小型發布系統,可以選擇由檔案事件流驅動(用於熱重載位元)。
上個世紀的 Perl/PHP 紳士駭客不會感到驚訝。
它的存在是因為有人吹著愚蠢的口哨並給犛牛剃毛。
這基本上就是它的作用(參考: shite_templating_publish_sources
函數)。
cat " ${watch_dir} /sources/ ${url_slug} " |
__shite_templating_compile_source_to_html ${file_type} |
__shite_templating_wrap_content_html ${content_type} |
__shite_templating_wrap_page_html |
${html_formatter_fn} |
tee " ${watch_dir} /public/ ${slug} .html "
# The complete "business logic" is 300-ish lines as of this comment,
# counted as all lines except comments and blank lines.
grep -E -v " s?#|^$ "
./bin/{events,metadata,templating,utils,hotreload}.sh |
wc -l
在你變得太興奮之前,我可以警告你,麻省理工學院的許可證意味著如果這個小垃圾製造商不能讓你的狗屎工作,我不必給予狗屎。貢獻中充滿了更多警告。
最後但並非最不重要的一點是,我特此下令,本文中的所有文本均以肖恩·康納利 (Sean Connery) 的方式閱讀。
在我shite
夢裡,我渴望…
最重要的是,保持它(“業務邏輯”)小。小到足以在我的腦海中緩存、調試和重構。
無需超級用戶許可即可安裝和使用。
極度避免工具鍊和建構依賴關係。沒有 gems/npms/venvs/what-have-yous。因此,Bash 是一種語言,因為 Bash 無所不在。當需要特定的進階功能時,也可以使用pandoc
或tidy
等標準套件。
在良好的 ol'heredocs 中使用普通 HTML 設定的無依賴模板。
簡單的元資料系統、內容命名空間、靜態資產組織等。
Web 伺服器可選(或任何類型的伺服器進程)。畢竟,我們的目標是靜態網站,它與file://
導航配合得很好。
用小的、可組合的、純功能的、類似 Unix 工具的部分來建構它,因為我非常喜歡這類東西。
為自己提供一個類似 REPL 的無縫編輯-保存-構建-預覽工作流程。
長時間停頓後,我不小心重新開始寫部落格。在我能夠將文字輸入雲端之前,我對「現代」靜態網站產生器感到困惑。因為 WordPress 太上個世紀了(或者我是這麼告訴自己的)。然後我對 SSG Jamstack 定制模板構建等魔法感到惱火。現在我正走在製作這個的黑暗道路上。它的部落格位址為:shite:來自 shell 的靜態網站:第 1/2 部分
我主要在「熱重載」模式下使用 shite,主要是為了撰寫貼文(在 org 模式下)並即時預覽它們(在 Firefox 中)。不太重要的是,熱預覽對樣式和/或頁面模板的修改。最重要的是,在無休止地寫一篇文章之後,我在「不熱重載」模式下使用它來進行完整的網站重建。
下面的示範範例。
基本上這意味著,如果我在sources
下創建、更新、刪除任何文件,它必須自動轉換為 HTML,在本地發佈到public
,並在我的網站打開的Web 瀏覽器中引起適當的頁面導航或重新加載操作。
在全新的終端機會話或 tmux 窗格中呼叫「主」腳本。
./shite.sh
根據我在./shite.sh
中的shite_global_data
數組中設定的預設值,它有助於在 Firefox 中開啟索引檔案。
在 Emacs 或 Vim 中,開啟sources
下的一些內容檔案。編輯、儲存並觀看瀏覽器中顯示的內容。 (是的,指定 Emacs/Vim 很愚蠢,因為我根據 inotify 事件觸發熱操作。顯然不同的編輯器對文件更新的方式不同。我使用Emacs 或Vim,所以我會監視它們引起的事件,因此它可以在我的機器上運行。
瀏覽器通常會記住滾動位置,這很整潔。有時熱重載是很糟糕的。所以我只需按空白鍵並儲存內容檔案即可再次觸發熱重載。
前往一些靜態資源,例如 CSS 樣式表。改變一些東西,例如背景顏色值。儲存並在瀏覽器中觀察顏色變化。
調整templates.sh
中的一些範本片段-例如部落格文章範本。然後切換到一些部落格文章內容檔案並修改它以使用修改後的模板觸發頁面構建(例如按空格鍵並保存)。
這是一個駭客行為。來源下的根index.org 頁面很特殊。如果我修改它,那麼這意味著我想重建索引頁、標籤的帖子列表,並重建相關的元文件,如 RSS feed、網站地圖、robots.txt 等。
在一個乾淨的新終端機會話中,使用「no」呼叫shite.sh
,並且可以選擇使用部署環境的base_url
:
重建「本機」file:/// 導覽的完整網站。真正的「無伺服器」:)
./shite.sh " no "
重建完整網站以在我的網域下發布。
./shite.sh " no " " https://evalapply.org "
這些標誌改變系統的行為。
SHITE_BUILD
設定為“hot”將在“monitor”模式下運行事件系統,從而驅動熱重載行為。將其設為“no”將抑制瀏覽器熱重載。SHITE_DEBUG_TEMPLATES
設定為「偵錯」將導致在發布任何模板化來源內容之前先取得範本。shite
內部非常 Unixy。或者說我是這麼認為的。
程式碼是函數式程式設計風格的 Bash。一切都是函數。大多數函數都是純函數——它們本身就是 Unix 的小工具。大多數邏輯都是面向管道的。這效果出奇的好,因為 Shell 對 FP 來說並不是一個壞地方。
在使用shite
編寫時,我還希望獲得類似 REPL 的即時互動體驗,因為我喜歡在 Clojure 和 Emacs 等即時/互動式運行時中工作。
因此, shite
已經成為這種完全反應式事件驅動的系統,能夠進行熱構建並在保存時重新加載。
目錄命名空間主要分為三:
sources
包含「來源」內容,例如用 orgmode 撰寫的部落格文章,以及 CSS、Javascript 和其他靜態資源。public
目標bin
URL 命名方案遵循sources
下的子目錄結構,並按原樣複製到pubilic
目錄結構下。由於這是 bog 標準 URL 命名空間方案,因此它也直接適用於已發佈的內容。就像這樣:
file:///absolute/path/to/shite/posts/slug/index.html
http://localhost:8080/posts/slug/index.html
https://your-domain-name.com/posts/slug/index.html
所有「公共」函數都被命名為shite_the_func_name
。所有「私有」函數都被命名為__shite_the_func_name
。
功能的存在是為了:
在一個乾淨的新終端會話中:
source ./bin/utils_dev.sh
shitTABTAB
或__shiTABTAB
進行自動補全。type -a func_name
以列印函數的定義並讀取其 API。shite_global_data
和shite_page_data
。範本適用於頁面片段(如頁首、頁尾、導覽)和完整頁面定義(如預設頁面範本)。這些內容以純 HTML 形式編寫在此處文件中。 ./bin/templates.sh
提供了這些。
模板填充有來自不同來源的變數資料:
shite_global_data
包含站點範圍的元數據, shite_page_data
包含特定於頁面的元數據。某些外部進程必須在處理任何頁面之前預先設定這些陣列。例如,一個完整的頁面可以建構如下:
cat ./sample/hello.md |
pandoc -f markdown -t html |
cat << EOF
<!DOCTYPE html>
<html>
<head>
$( shite_template_common_meta )
$( shite_template_common_links )
${shite_page_data[canonical_url]}
</head>
<body ${shite_page_data[page_id]} >
$( shite_template_common_header )
<main>
$( cat - )
</main>
$( shite_template_common_footer )
</body>
</html>
EOF
shite
的元資料系統被定義為鍵值對。鍵命名元資料項,並將與該類型的任何值相關聯。下面的例子。
如前所述,運行時元資料由關聯數組shite_global_data
和shite_page_data
在環境中攜帶。這些可以透過直接建置來填充,也可以從外部來源更新。
每個頁面可以在頁面頂部的「前面的內容」中指定自己的元資料。這將與從其他來源派生的頁面元資料一起使用。
shite
希望我們使用與給定內容類型相容的語法來編寫前文,如下所示。
使用註解行# SHITE_META
來劃分shite
也應該解析為特定於頁面的元資料的組織風格元資料。
# SHITE_META
#+title: This is a Title
#+slug: this/is/a/slug
#+date: Friday 26 August 2022 03:38:01 PM IST
#+tags: foo bar baz quxx
# SHITE_META
#+more_org_metadata: but not processed as shite metadata
#+still_more_org_metadata: and still not processed as shite metadata
* this is a top level heading
this is some orgmode content
#+TOC: headlines 1 local
** this is a sub heading
- this is a point
- this is another point
- a third point
寫出 Jekyll 風格的 YAML 前面的內容,放在---
分隔符號之間。
---
TITLE : This is a Title
slug : this/is/a/slug
DATE : Friday 26 August 2022 03:38:01 PM IST
TAGS : foo BAR baz QUXX
---
# this is a heading
this is some markdown content
## this is a subheading
- this is a point
- this is another point
- a third point
我們可以簡單地使用標準<meta>
標籤,遵守以下約定: <meta name="KEY" content="value">
。
< meta name =" TITLE " content =" This is a Title " >
< meta name =" slug " content =" this/is/a/slug " >
< meta name =" DATE " content =" Friday 26 August 2022 03:38:01 PM IST " >
< meta name =" TAGS " content =" foo BAR baz QUXX " >
< h1 > This is a heading </ h1 >
< p > This is some text </ p >
< h2 > This is a subheading </ h2 >
< p >
< ul >
< li > This is a point </ li >
< li > This is another point. </ li >
< li > This is a third point. </ li >
</ ul >
</ p >
這裡是犛牛!
完全被 Clojure/Lisp/Spreadsheet 風格的即時互動式工作流程寵壞了,我也想在製作中進行熱重載和熱導航。
但似乎不存在一個獨立的即時 Web 開發伺服器/工具,它不希望我下載一半的已知網路作為依賴項。正如我之前所說,這是我非常不想做的事。
DuckSearch 提供了 Emacs 不耐煩模式,這非常熱門,但我不想將其硬連線到我的 Emacs。幸運的是,它還提供了這個令人興奮的腦波,其中包括“inotify-tools”和“xdotool”:github.com/traviscross/inotify-refresh
火熱副本!
因為還有什麼比我的電腦對我按 F5 鍵更熱的呢?彷彿它知道我內心深處真正想要的是什麼。
事件子系統與其他所有子系統正交,並與系統的其餘部分組合在一起。
該設計是沼澤標準流架構,即。監視檔案系統事件,然後過濾、刪除重複、分析並將它們路由 (tee) 到不同的事件處理器。目前只有兩個這樣的處理器;一個用於編譯和發布與事件關聯的頁面或資產,另一個用於根據相同事件熱重新載入瀏覽器(或熱導航)。
基本上是這樣的:
# detect file events
__shite_detect_changes ${watch_dir} ' create,modify,close_write,moved_to,delete ' |
__shite_events_gen_csv ${watch_dir} |
# hot-compile-and-publish content, HTML, static, etc.
tee >( shite_templating_publish_sources > /dev/null ) |
# browser hot-reload
tee >( __shite_hot_cmd_public_events ${window_id} ${base_url} |
__shite_hot_cmd_exec )
事件只是一個 CSV 記錄流,結構如下:
unix_epoch_seconds,event_type,base_dir,sub_dir,url_slug,file_type,content_type `
我們使用事件記錄的不同部分來引發不同類型的操作。
前面連結的 inotify-refresh 腳本嘗試定期刷新一組瀏覽器視窗。然而,我們希望非常渴望。對我們的內容文件和/或靜態資產的任何編輯操作都必須在顯示我們的內容的瀏覽器標籤中即時觸發熱重載/導航操作。
我們想要定義不同的重新載入場景:互斥的、集體詳盡的儲存桶,我們可以將要監視的檔案事件對應到其中。
如果我們這樣做,那麼我們可以將更新建模為一種預寫日誌,透過分析管道沖壓事件,將它們與完全匹配的場景關聯起來,然後最終引發操作。例如:
刷新當前選項卡時
回家甚麼時候
導航至內容時
由於我們讓電腦模擬我們自己的鍵盤操作,因此它可能會幹擾我們個人的操作。如果我們堅持在文字編輯器中寫我們的狗屎,並讓電腦來做熱重載的事情,我們應該保持不生氣。
世界上有很多犛牛。
對於真正普遍的多站點發布魔力:
shite
應該在我的 PATH 上可用這是一頭小犛牛。我可能很快就會把它剃掉。
顯然,人們可以使用流行的 git 主機的 CI 作業來觸發shite
建置。但是,當我們已經進步到 1900 年代末的最先進技術時,為什麼還要使用笨重的本世紀技術……完全流式傳輸和完全反應式呢?
除了諷刺之外,我不明白為什麼不能在我運行的遠端電腦上使用相同的事件系統來添加熱部署支援。
在遠端盒子上:
sources
的克隆被供奉sources
(減去瀏覽器觀看)是即時的。在我的本地盒子上:
https://mydomain.com/posts/hello/index.html
上按 F5透過 SSH 執行某些操作,將瀏覽器刷新帶回本地,以防熱部署到遠端伺服器。
也許是一些“開發/起草”時間設定/拆卸場景?也許我們用一個「dev_server」函數來啟動一個新的垃圾寫作會話?
如果你一路走到這裡,仍然想做出貢獻......
為什麼?
為什麼以一切神聖和美好的名義,你想要這麼做?這不是很明顯是個傻瓜的作品嗎?您沒有聽說過 Bash 甚至不是真正的程式語言嗎?難道你的公關將永遠消失,你的評論將陷入無名的空白,這不是顯而易見的嗎?
是的,發送補丁是一個糟糕的主意。
但請透過電子郵件告訴我你對你的屎製造商的希望和夢想!我在 gmail 上以我的名字點姓氏閱讀電子郵件。
我們可以一起吹口哨,用我們自己獨特的方式吹著愚蠢的曲子,一起為我們各自的犛牛剃毛。
願源頭與我們同在。
本作品獲得 MIT 授權和 CC By-SA 4.0 授權的雙重授權。
SPDX 許可證識別碼:mit 或 cc-by-sa-4.0