20251229 越來越沒辦法完全的推薦別人使用 Hugo 了
Published:Updated:
這篇雖然屬於「Hugo 的問題」系列,但是這篇沒有任何對開發者回應方式的不滿,本文夾雜在幾篇不滿之間,因此特別加上澄清。
開發者在這裡的回應我完全可以接受,我不是文字遊戲大師在每句話裡面都要挑毛病。
Hugo 要用戶踩過坑才知道的點太多,本文不是普通用戶的設定踩坑文章,是專案維護者的踩坑體驗。
正在撰寫中的 Hugo 系列文章,在裡面我一點也不推薦企業選擇 Hugo,但是對一般用戶我覺得還是可以推薦的,但是本文遇到的行為又讓我對推薦 Hugo 給一般用戶扣更多分。
Hugo 的文檔我可以幫他總結成一句話,老子不寫就是不寫。
Hugo 主題安裝
就是 Git submodule 或是 Hugo module,兩者都是接入到 Hugo 的 UFS 系統中,簡單來說就是如果有相同檔案,越靠近專案根的會覆蓋越遠的。
Hugo module 官網說他就是個 go module,但是大多數用戶都不是開發者了,就算是開發者也不見得寫過 go,因此我一向不推薦用 go module 安裝,不過我為了解決此 issue因此測試以 Hugo module 安裝。
奇怪的行為
測試過程中使用 Hugo v0.150.0 新推出的指定版本功能,然而實際測試指定版本功能安裝主題卻出現這樣的結果:
1hugo new site mynewsite --quiet
2cd mynewsite
3
4cat << EOF >> hugo.toml
5[module]
6[[module.imports]]
7path = 'github.com/spf13/hyde'
8version = 'v1.3.0'
9EOF
10
11hugo mod init foo
12hugo mod get
13
14# 輸出結果
15go: creating new go.mod: module foo
16hugo: to add module requirements and sums:
17 hugo mod tidy
18go: no module dependencies to download
19go: added github.com/spf13/hyde v1.4.1
奇怪的是指定的版本和命令行的輸出不同,進一步測試結果也不一樣:
❯ hugo mod graph
project github.com/spf13/hyde@v1.3.0+vendor
❯ go mod graph
foo github.com/spf13/hyde@v1.4.1
foo go@1.25.5
go@1.25.5 toolchain@go1.25.5
最後用 hugo mod vendor 把依賴 dump 到 _vendor 目錄,裡面檔案有標記版本才終於確定 Hugo 使用了正確的版本。
問題是什麼?
我完全能接受如果有各式各樣奇怪的阻礙讓這些輸出無法一致,我不是完美主義者,但是問題在於文檔,你看他的文檔是怎麼寫的:
New in v0.150.0
If set to a version query, this import becomes a direct dependency, in contrast to dependencies managed by Go Modules. See this issue for more information.
Version query 連結是 Golang 的 semantic versioning,與此問題無關;下一句「變成直接依賴,與 Go module 管理不同」誰會想的到不給 Go 管理 Go 還是會印出來?而且 go.mod go.sum 一樣會記錄這個依賴,一般用戶誰想的到這種行為?好吧可能他提供的 issue 裡面有說明:
Fixes #13964
Notes
versioncan be blank or set to a Go Module version query. It's recommended to stick to tagged version values following Go Modules version numbering scheme, e.g.v1.0.0. This enables module caching and you avoid having to download the module every time you runhugo. Also see module vendoring.- If
versionis set, we call it a direct dependency, in contrast to dependencies managed by Go Modules. - With only direct dependencies (including its transitive dependencies), you don't need a
go.mod. For transitive dependencies, see noimports. - You can import the same
pathmultiple times with different values ofversion, but you need to think about how to mount the different versions into your project. But note that in the first version of this new feature you can only have one import per resolved version of a givenpath, but each import can have multiple mount definitions. Also see the [nomounts].(gohugo.io/configuration/module#nomounts) config setting. - The command
hugo mod getwill only work on module imports without aversionset, so for these you either need to edit the config file or set a loose version query (e.g.main,latest,>v1.0.0) . Note that Go Module's Minimum Version Selection (MVS) is not relevant for these imports.
TODOs
- vendoring with
hugo mod vendor. -
go.sum; imports withversionset will not en up ingo.{mod,sum}. The first can be a good thing (because you don't need ago.modfile for simpler setups). But without asumentry, we don't get the security it gives, which I think is worth going the extra mile. I think that the tooling for this should be relatively straight forward and that we could create an extrahugo.direct.sum(Make hugo.mod the new go.mod #13971 will give us ahugo.sum) file for these entries). - Decide how the path printed in
go mod graph,hugo config mounts,vendor.txtshould look like this; I think the pattern I currently use for vendoring will be OK and give the most flexibility re.--ignoreVendorPathsmatching and would make it unique. See example below. - Add some tests.
Sample vendor.txt:
# github.com/bep/hugo-mod-misc/dummy-content v0.3.0
# github.com/bep/hugo-mod-misc/dummy-content@v0.1.0 v0.1.0
# github.com/bep/hugo-mod-misc/dummy-content@v0.2.0 v0.2.0
The entries resolved via a version in config have a pseudo path constructed with [path]@[resolved version].
Hugo 提供的是一個被 closed 的 PR,裡面有八個連結,而且 again 沒有提到不一致的問題,最誇張的是提供一個被 closed 的 PR 當作文檔細節到底誰知道他想表達什麼?被 closed 了誰知道這內容到底有效還是無效?誰有閒為了一個簡單的 version 設定閱讀這麼多內容?我也完全理解 PR 描述技術細節沒問題,問題是你文檔根本不解釋也不摘要,就丟了一個 PR 給用戶看,who the fuck knows 你到底想講啥?
實際上文檔也不用那麼複雜,簡單提到 hugo.direct.sum 這整段文章都不用寫了,作為一個版本管理工具卻有不同的版本記錄,因為技術限制無法做到那我能接受,但是你文檔也不寫就要用戶猜。
你要說那個版本改了一堆東西因此文檔沒做好那也就算了,不,那個版本就只有這個 feature,老子不寫就是不寫。
Tailwind 整合也有問題
看測試:
@import "foo.css";OK@import "./foo.css";OK@import "foo.css" layer(foo);fail@import "./foo.css" layer(foo);fail@import "css/foo.css";fail@import "./css/foo.css";fail@import "assets/css/foo.css";fail@import "./assets/css/foo.css";fail@import "./assets/css/foo.css" ;OK (empty space)@import "assets/css/foo.css" ;fail (empty space)@import "./assets/css/foo.css" layer(foo);OK@import "assets/css/foo.css" layer(foo);fail
我知道這個問題可以很簡單的解決:
@layer foo {
@import "./foo.css";
}
但是我正在處理的是上游主題分發給下游的問題,我希望語法一致,並且從根本解決問題。
發 issue 後 Maintainer 回我我的測試看起來沒有道理,我也知道這看起來很沒道理,我的目的是好心提供測試結果給他看讓他一看就知道問題是啥不用再浪費時間理解和測試,為了保證這測試正確還特地改成新版用 pkg 安裝(pkg 我安裝完還要自己刪因為我在用的 Hugo 是手動 build 的)
回到問題本身,Maintainer 對這個問題的說明是
Hugo's inline import resolver is very simple, as mentioned (I think) in the docs.
好傢伙問題又在文檔了,我們來看看文檔寫了啥:
disableInlineImports
(bool) Whether to disable inlining of @import statements. Inlining is performed recursively, but currently once only per file. It is not possible to import the same file in different scopes (root, media query, etc.). Note that this import routine does not care about the CSS specification, so you can have @import statements anywhere in the file. Default is false.
skipInlineImportsNotFound
(bool) Whether to allow the build process to continue despite unresolved import statements, preserving the original import declarations. It is important to note that the inline importer does not process URL-based imports or those with media queries, and these will remain unaltered even when this option is disabled. Default is false.
根本就沒提到。我只是想要用 CSS cascade layer,誰知道有這麼多奇怪的怪癖?直接不行用就算了至少一番兩瞪眼,這是有奇怪的解析行為讓用戶混淆。
該怎麼解決文檔問題?
解決不了,我多篇文章都對 Hugo 給過同樣的評論:
- 專案 10 年來都只有兩個人在維護,這種狀態不能算是健康
- 前端工具需要高強度 Golang 知識,更限縮社群參與者
- 文檔非常糟糕,早就說過:他們都不覺得自己把文檔完善之後可以少回答很多問題嗎?
- 好心寫了 Thoughts about Hugo’s documentation,對編排和外觀佈局給出具體建議,給出我認為很好的文檔方案和範例,撰寫時就考慮到寫這麼多會不會被當作在抱怨因此多次潤飾,結果被 maintainer 回這種文章讓人 demotivating,當下我還想說該道歉,現在覺得我真不該覺得抱歉,哪個 hater 會附上這麼多資訊?他自己連具體建議和 hater 都分不出來,現在是要怪提供建議的人?還是都不要建議算了?
- 這麼會抱怨為什麼不貢獻文檔?因為這是系統性、文檔編排、文檔細節、網站外觀全部都要調整的東西,我一個路人怎麼可能貢獻?
- 當時沒意識到,撰文同時把這些事實連起來之後發現 Hugo 文檔的概念是「老子不寫就是不寫」,綜合這些事實可以判斷 Hugo 文檔是永遠不可能改善的,連只有一個 feature 的 major release 都不寫文檔甚至還丟 closed PR 給你,你還期待他寫啥文檔?丟被關閉的 PR 當作補充真的是我看過最厲害的操作沒有之一。
每篇文章都要再說一次的是,我從不否定維護者對 Hugo 的付出,他們的付出非常偉大,我甚至好幾次使用時心裡都在感謝他們,但是這不改變文檔非常糟糕的事實,我當然也知道開源軟體沒欠我,但這和我罵他們的爛文檔沒關係。
這篇雖然屬於「Hugo 的問題」系列,但是這篇沒有任何對開發者回應方式的不滿,本文夾雜在幾篇不滿之間,因此特別加上澄清。
開發者在這裡的回應我完全可以接受,我不是文字遊戲大師在每句話裡面都要挑毛病。