我开发了一个静态网站生成器:Zest — 模板即代码,由 F# 驱动的 SSG

项目分享
#F# #Zest #开源 #.NET
发布于 2026-06-24 3 分钟阅读 988 字

我写 Zest SSG 的经过和想法

项目头图上是一只小图标,名字叫 Zest SSG,全称 Zealous Efficient Static Toolkit(热情高效的静态工具包)。它本质上是个混用 F# 和 C# 的静态网站生成器。我最核心的一个念头是:模板应该是真正的代码,而不是一堆字符串。写模板的语言和主语言,本就该是同一门。

为啥要搞这么个东西

开发这玩意儿,是因为我对市面上不少静态站点生成器越来越不耐烦。很多工具非得让你写 YAML 配置,模板又是另一套怪兮兮的字符串替换语法。我平时用 F#,心里就嘀咕:构建网站时,为啥不能直接跑 F# 脚本呢?于是 Zest 的页面文件变成了 .zest.fsx——它们是实打实的 F# 脚本,在构建时通过 dotnet fsi 执行。你可以随心所欲用 F# 的列表推导、模式匹配、字符串插值,想算什么算什么。

不过我也明白,不是每次都得兴师动众启动 FSI。所以加了 .zhtml 轻量页面:纯 HTML 加可选的 Nunjucks 语法,没有额外开销。还有 HTML DSL,能声明式地写 render [ h1 []; p [] ]。甚至能在 .zest.fsx 里直接用 md 助手写 Markdown,跟 HTML DSL 混在一起用。

样式、配置和其他模板

样式这块,我不想引 Sass、Less 那些外部依赖,就自己设计了 ZCSS。它是 CSS 的超集,支持嵌套、F# 风格的 let 绑定、数学表达式、颜色函数和 mixin,最后编译成标准 CSS。比如你可以写 let space4 = 0.25r * 4,算好再塞进样式里。

配置上我相当固执:坚决不用 YAML,只使用 TOML。项目里有个可选的 _init.zest.fsx,在构建前跑,能注入动态数据、读 JSON/TOML、读环境变量。此外,考虑到有些人习惯别的模板语言,我接进了 Nunjucks、Liquid、Handlebars、Mustache、HAML、Pug、WebC,全部自动转成 Nunjucks 引擎处理。这活儿挺琐碎,但能让用惯那些语言的人也能上手。

主要功能罗列一下:

  • 模板即代码:.zest.fsx 是真 F# 脚本。
  • .zhtml 轻量页:纯 HTML 带可选 Nunjucks,无 FSI 负担。
  • HTML DSL:声明式组合 HTML。
  • 内联 Markdown:在脚本里直接混写。
  • 标准 .md 文件,带 frontmatter。
  • ZCSS:带嵌套、变量、数学、颜色函数。
  • 兼容 11ty 系模板:上面说的那堆都支持。
  • _init.fsx 初始化脚本。
  • TOML 配置,零配置可用。
  • 热重载、批量评估(多脚本单进程跑)、增量构建、跨平台。

怎么用,怎么搭

快速开始就几句:

zest init my-site
cd my-site && zest serve --port 8080
zest build
zest preview

举个小例子,.zest.fsx 里可以写:

let pageTitle = "Hello from F#"
let items = ["F#"; "Zest"; "SSG"]
render [
    h1 [ text pageTitle ]
    p  [ text "This page is generated by real F# code at build time." ]
    ul [ for i in items -> li [ text i ] ]
]

ZCSS 例子:

let primary    = #3b82f6
let space1     = 0.25r
let space4     = space1 * 4
let primary-light = primary |> lighten(45%)
.tag
  color: $primary
  background-color: $primary-light
  padding-block: $space4
  border-radius: 9999px

编出来就是普通 CSS。

架构分四块:Zest.App(C# 命令行入口)、Zest.Engine(F# 核心逻辑)、Zest.Dsl(F# 预编译辅助)、Zest.Infra(C# 管配置和文件监听)。这样拆是因为 C# 弄命令行省事,F# 弄计算和 DSL 顺手。

我较劲的地方

写的时候,有几条设计哲学是我刻意坚持的:

  1. F# 做模板——模板就是程序本身。
  2. ZCSS 是布局引擎,不是传统预处理器,它吐出 CSS。
  3. TOML 做契约,永不 YAML。这点我尤其硬,因为以前被 YAML 缩进坑惨过。
  4. JavaScript 只管客户端交互,不碰 Node、npm、打包器。
  5. 这工具是给少数爱 F#、恨 YAML、喜欢简单东西的人做的。

开发中途有段插曲:一开始每次构建都开新 FSI 进程,慢得我想摔键盘。后来改成批量评估,多个脚本搁一个进程里跑,速度才上来。增量构建也是后来补的,只重建改动的文件。

致谢

这项目能成形,多亏了我几个能干的 AI 助手,它们帮我做设计、调试和文档:

  • Claude:架构和设计评审
  • GLM:调研和实验
  • DeepSeek:调试和性能调优
  • Hunyuan:实现和文档支持

真心谢谢它们。代码以 Apache 2.0 许可开源。