CUE 能做一切,但它能生成可读文档吗?

发布: (2026年1月13日 GMT+8 17:07)
9 min read
原文: Dev.to

Source: Dev.to

请提供您希望翻译的具体文本内容,我将为您翻译成简体中文,并保持原始的格式、Markdown 语法以及技术术语不变。谢谢!

CUE – 文件生成(以及文学化编程)的瑞士军刀

CUE 是当你需要:

  • 生成复杂的 JSON,
  • 验证 YAML,
  • 确保你的整数真的就是整数,

以及,令人惊讶的是,当你想要一个 文学化编程 工作流时,会选择的工具。

为什么要使用文学化编程工具?

当前文学化编程的“王者”是 org‑mode
它在 Emacs 内部 工作得很好,但一旦你尝试把文档分享给使用 VS Code(或其他编辑器)的人,就会陷入厂商锁定。

你希望你的文档——你的 文学化代码——是 可移植 的,而不是只能在某个编辑器里起作用的魔法咒语。

当你写关于代码的内容(博客文章、教程、内部文档)时,通常不会一次性倾倒一个巨大的文件。相反,你会:

  1. 展示一段逻辑代码片段。
  2. 展示一段配置代码片段。
  3. 也许还有一段 CSS。

然后用文字把这些零散的部分串联起来。

“复制‑粘贴致敬”

你写了一篇精彩的教程,把可运行的代码复制进 Markdown 文件,随后又把一个变量改名(ttimeout)。你修正了源码,却 没有 修正 Markdown。读者复制的代码已损坏,教程不再可信,而你显得很马虎。

CUE 通过让我们定义 部件以编程方式 将它们 拼接 在一起,解决了这个问题。它不仅仅保存文本——它会验证这些片段是否真正匹配,确保你在解释中展示的代码正是最终构建中使用的代码。

可以把它想象成一套乐高积木,如果结构不稳固,砖块就会拒绝卡合。¹

Source:

停止致敬:将文档视为构建目标

CUE 的 tool/file 用于创建文件,而 tool/exec 用于运行命令。
我们并非仅仅在对字符串进行模板化,而是在定义一个 依赖图。如果任何依赖(例如代码片段)无效,文档根本就不会生成。

示例 1 – 使用动态版本生成 Markdown 文件

package example

import (
    "tool/file"
    "tool/exec"
)

// Build command – for every entry in the `files` struct, create a file.
command: example: {
    for k, v in files {
        "(k)": file.Create & {
            filename: k
            contents: v
        }
    }
}

// Run a shell script to obtain a version number.
data: exec.Run & {
    cmd:   ["sh", "-c", "echo '1.2.3'"]
    stdout: string
}

// Final document, stitched together.
// `(data.stdout)` is a promise that `data` will run before the string is finalized.
files: {
    "output.md": """
My Awesome Project

Current version: (data.stdout)
"""
}

没有 CUE:你需要手动输入 1.2.3。如果版本后来改为 1.2.4,文件就会变得过时,除非你再次手动编辑。

使用 CUE:如果 data.stdout 失败(例如 shell 命令崩溃),文件将不会生成,构建会失败——从而保护读者不受到错误信息的影响。

运行构建 – cue cmd

CUE 是声明式的:它描述期望的状态,但 不会自行改变它。file.Createexec.Run 只是数据,直到你调用 任务引擎 为止:

cue cmd

cue cmd 是 CUE 中唯一允许产生副作用的部分。它:

  1. 查找 command: 块。
  2. 顺序 执行任务。
  3. 在 “我想要一个文件”(定义) 与 “这是你的文件”(实际) 之间架起桥梁。

重要:为了让 cue cmd 识别你的命令,文件名 必须以 _tool.cue 结尾(例如 build_tool.cue)。如果命名为 example.cue,CUE 将完全忽略这些命令。

扩展 CUE – 使用 tool/exec 的自定义渲染器

简单的 shell 命令适用于玩具示例,但真实项目需要更强大的功能(例如,编译 Haskell、渲染图表)。CUE 让将任意 CLI 工具包装成 渲染器 变得轻而易举。

示例 2 – 多语言渲染器

package example

import (
    "tool/exec"
)

// Renderers – wrappers around CLI tools.
// Each renderer takes a `code` string and pipes it into a command.

renderers: pikchr: exec.Run & {
    cmd:    ["pikchr", "--svg-only", "-"]
    code:   string
    stdin:  code
    stdout: string
}

renderers: haskell: exec.Run & {
    cmd:    ["stack", "runghc"]
    code:   string
    stdout: string
    // Wrap the snippet in a module so it compiles standalone.
    stdin: """
module Program where
(code)
"""
}

renderers: bash: exec.Run & {
    cmd:    ["bash", "-e"]
    code:   string
    stdin:  code
    stdout: string
}

// And, because sometimes Bash isn’t hip enough…
renderers: zsh: exec.Run & {
    cmd:    ["zsh"]
    code:   string
    stdin:  code
    stdout: string
}

这能为你提供的功能:在 CUE 配置中拥有一个小巧的自定义 CI 运行器。现在你可以:

  • 将 Pikchr 图表渲染为 SVG。
  • 即时编译并运行 Haskell 代码片段。
  • 安全地执行 Bash 或 Zsh 脚本。

测试与 CI

现在,当我想要插入图表或代码输出时,只需把代码片段交给相应的渲染器。CUE 负责调用 shell、通过 stdin 传递输入并捕获 stdout,完成繁重的工作。它把你的文档变成了一个活的程序。

最棒的地方在于,你可以 “测试” 你的文章。运行 cue cmd example 时,CUE 实际上会执行 shell 命令、编译 Haskell、渲染 Pikchr 图表,并生成最终文件。

  • 如果你的代码片段有语法错误,生成会失败。
  • 如果你的 Haskell 代码无法编译,生成会失败。
  • 如果你尝试用无效坐标创建 Pikchr 图表,生成会失败。

它把“写博客”变成了“通过 CI/CD 流水线”。

这有点像拥有一个既是校对员又是极其严格的编译器 2。它确保 我文章中的每个图表都是真正由旁边的代码渲染出来的——没有过时的图片,没有手动导出,也没有我忘记写下的“魔法”步骤。它不依赖任何特定的编辑器生态系统,实际上解决了文档的“在我的机器上能跑”的问题。你不仅仅是在写作;你在对文本进行工程化。

或者如果你想把 Megablok 放在 Lego 上。CUE 有很高的标准,绝不会犹豫去评判你拙劣的结构选择。 ↩

而且不同于人工校对员,CUE 不会要求薪水、喝光你的咖啡,或抱怨你过度使用双关。 ↩

要点

  • CUE 让你把文档视为 一等构建产物
  • 你在正文中展示的代码 有保证 与实际运行的代码一致。
  • 通过使用 cue cmd 并将文件命名为 *_tool.cue,你可以获得可复现、对副作用敏感的流水线。
  • 使用 tool/exec 扩展 CUE 意味着你可以封装任何所需的语言或工具,使你的文学化编程工作流既 可移植可靠

注脚

  1. “乐高套装中,如果你构建的结构不稳固,砖块就会拒绝卡合”是对 CUE 验证机制的比喻:如果底层部件不满足约束,它不会让你生成文件。
Back to Blog

相关文章

阅读更多 »

爱恨信 do Json

将 JSON 用作配置格式是一个错误。是的,一个错误。不是偏好,也不是风格选择。是一次架构错误。该说法倾向于同时……