在日常开发中,我们经常需要把 Markdown 文档转为 HTML——比如给团队写技术周报、导出接口文档、发布博客。每次打开在线转换工具来回切换窗口很麻烦。
今天用 OpenCode 写一个命令行工具 md2html,指定输入 Markdown 文件,一键输出 HTML 文件,同时支持管道输入和实时预览服务器。技术栈:Go + goldmark 库,最终代码约 120 行。
打开终端,新建项目目录:
mkdir md2html && cd md2html go mod init md2html
然后用 opencode 启动 AI 编程助手,开始对话。
我对 OpenCode 说:
> 帮我用 Go 写一个命令行工具 md2html,接收一个 Markdown 文件路径,转换为 HTML 并输出到同名 .html 文件。使用 goldmark 库做转换。
OpenCode 分析了需求后,先添加依赖:
go get github.com/yuin/goldmark
然后生成了 main.go 的基础骨架——读取文件、调用 goldmark 转换、写入输出文件。代码简洁,核心逻辑仅 30 行。
要点:描述清楚输入(文件路径)和输出(.html 文件),OpenCode 会自动选择合适的库和方法。
我对 OpenCode 说:
> 在原有基础上,增加从 stdin 读取 Markdown 内容的功能。如果没有提供文件参数,就从标准输入读取,转换后的 HTML 输出到 stdout。
OpenCode 修改了 main 函数,用 os.Args 判断参数数量:
os.Stdin 输入,os.Stdout 输出这让工具更灵活了,可以配合管道使用:
echo "# Hello" | md2html cat README.md | md2html > output.html
要点:用自然语言描述"如果……就……"的条件逻辑,OpenCode 能准确转化为代码分支。
我对 OpenCode 说:
> 给输出 HTML 包一层完整的 HTML5 模板,包含基本的 CSS 样式(使用 GitHub 风格的字体和代码块高亮),标题用 Markdown 文件名。
OpenCode 定义了两个 HTML 模板字符串——一个给文件模式用(完整 HTML 页面),一个给管道模式用(纯片段)。模板中嵌入了 GitHub 风格的暗色代码块样式和系统字体栈。
生成的效果和 GitHub README 预览非常接近。
要点:指定样式风格时,可以用知名产品作为参考("GitHub 风格"),OpenCode 能理解并实现。
我对 OpenCode 说:
> 再加一个 serve 子命令,启动本地 HTTP 服务器,实时显示当前目录下 README.md 的渲染结果。
OpenCode 新增了 serveCmd 处理逻辑,用 net/http 启动服务器,请求时动态读取 Markdown 文件、实时转换并返回 HTML。打开浏览器就能预览,修改 Markdown 后刷新页面即可看到最新效果。
这是最让我惊喜的部分——我只说了"实时显示",OpenCode 就自动选择了 HTTP 服务器方案,而不是轮询或复杂的文件监控。
要点:不要过度约束实现方式,让 AI 发挥它的判断力。
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"strings"
"github.com/yuin/goldmark"
)
const pageTpl = `<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>%s</title>
<style>
body { max-width: 860px; margin: 40px auto; padding: 0 20px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; font-size: 16px; line-height: 1.7; color: #24292e; }
pre { background: #1e1e1e; color: #d4d4d4; padding: 16px; border-radius: 6px; overflow-x: auto; font-size: 14px; line-height: 1.5; }
code { background: #f6f8fa; padding: 2px 6px; border-radius: 3px; font-size: 85%%; }
pre code { background: none; padding: 0; }
table { border-collapse: collapse; width: 100%%; }
th, td { border: 1px solid #dfe2e5; padding: 8px 12px; text-align: left; }
th { background: #f6f8fa; }
blockquote { border-left: 4px solid #dfe2e5; padding: 0 16px; color: #6a737d; margin: 0; }
</style>
</head>
<body>
%s
</body>
</html>`
func main() {
if len(os.Args) > 1 && os.Args[1] == "serve" {
serveCmd()
return
}
md := goldmark.New()
if len(os.Args) > 1 {
src, err := os.ReadFile(os.Args[1])
if err != nil {
fmt.Fprintf(os.Stderr, "读取文件失败: %v\n", err)
os.Exit(1)
}
var buf bytes.Buffer
if err := md.Convert(src, &buf); err != nil {
fmt.Fprintf(os.Stderr, "转换失败: %v\n", err)
os.Exit(1)
}
outName := strings.TrimSuffix(filepath.Base(os.Args[1]), filepath.Ext(os.Args[1])) + ".html"
html := fmt.Sprintf(pageTpl, filepath.Base(os.Args[1]), buf.String())
if err := os.WriteFile(outName, []byte(html), 0644); err != nil {
fmt.Fprintf(os.Stderr, "写入文件失败: %v\n", err)
os.Exit(1)
}
fmt.Printf("已生成: %s\n", outName)
} else {
src, err := io.ReadAll(os.Stdin)
if err != nil {
fmt.Fprintf(os.Stderr, "读取标准输入失败: %v\n", err)
os.Exit(1)
}
var buf bytes.Buffer
if err := md.Convert(src, &buf); err != nil {
fmt.Fprintf(os.Stderr, "转换失败: %v\n", err)
os.Exit(1)
}
fmt.Print(buf.String())
}
}
func serveCmd() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
src, err := os.ReadFile("README.md")
if err != nil {
http.Error(w, "README.md 不存在", 404)
return
}
var buf bytes.Buffer
md := goldmark.New()
if err := md.Convert(src, &buf); err != nil {
http.Error(w, "转换失败", 500)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprintf(w, pageTpl, "README.md", buf.String())
})
fmt.Println("预览服务器已启动: http://localhost:8080")
http.ListenAndServe(":8080", nil)
}
从零到完整可用的 Markdown 转 HTML 工具,全程只用了 4 次对话。我没有写一行代码,也没有查 goldmark 的 API 文档——OpenCode 自动完成了依赖安装、代码生成和错误处理。
对比传统开发方式:手动查文档 10 分钟 + 写代码 20 分钟 + 调试样式 10 分钟 = 至少 40 分钟。用 OpenCode,5 分钟搞定。
关键心得:把 OpenCode 当作结对编程的伙伴,用自然语言描述"做什么"而非"怎么做",让它发挥对库和最佳实践的了解,效率远超逐行手写。