dsh-template-render
v0.1.0
Published
Template rendering for DeepSeek Harness: render `{{ var }}` placeholders with filters, `{% if %}` branches and `{% each %}` loops into text; validate templates for syntax and undefined variables; extract required data fields. Zero runtime dependencies, re
Maintainers
Readme
dsh-template-render
DeepSeek Harness 的模板渲染工具箱:render_template 用数据把 {{ var }}、{% if %}、{% each %} 渲染成文本,validate_template 在发布前静态检查语法与未定义变量,extract_vars 列出模板需要哪些数据字段。只读、零运行时依赖,不 eval、不执行代码。
- 源码:https://github.com/geeklei/dsh-plugins/tree/main/dsh-template-render
- npm:https://www.npmjs.com/package/dsh-template-render
功能说明
很多文本是「骨架 + 变量」:发一封邮件、生成一段配置、拼一条上报,正文基本固定,只有姓名、数值、状态在变。手写字符串拼接容易错、难维护,正则替换又处理不了「有就列出来、没有就换句话」的分支。
本插件用一套极小的模板语法把「文本」和「数据」分开:
- 变量占位
{{ name }}:用数据里的值替换,支持点路径({{ a.b.c }})、数组下标({{ items.1 }}),还能套过滤器({{ n | round:1 }})。 - 条件分支
{% if 条件 %}…{% else %}…{% endif %}:条件可以是任意变量(真/假),也支持==、!=、>、>=、<、<=比较。 - 循环
{% each 列表 as 项 %}…{% empty %}…{% endeach %}:按列表逐项重复,空列表走empty分支;循环内可用{{ item }}、{{ item.字段 }}和{{ for.index }}/{{ for.first }}/{{ for.last }}。 - 注释
{# 说明 #}:渲染时直接移除,适合在模板里留说明。 - 转义
\{{:想输出字面{{时用反斜杠转义。 - 过滤器:
upper、lower、trim、length、json、number、round:n、default:x、join:分隔、first、last。
解析器是自写分词 + 递归渲染,不做 eval,不加载模块,模板里的内容只是字符串和数据,不会被执行。
安装
npm install dsh-template-render安装后在 cordis.patch.yml 中加入:
- insert:
- id: template-render
name: "dsh-template-render"使用说明
render_template
把模板和一份数据渲染成文本。模板用 text 传正文或 path 传文件,数据用 data 传 JSON 字符串或 data_path 传 JSON 文件(缺省为空对象 {})。strict=true 时遇到未定义变量会报错,否则渲染为空串。
参数:text / path(模板,二选一,text 优先);data / data_path(数据,二选一,data 优先);strict(默认 false);max_chars / max_size。
模板来源:(内联模板);数据来源:(内联数据)
--- 渲染结果(4 行)---
报告:智慧城市一期
姓名:甲(架构)
规模较大
无备注
--- 统计 ---
已替换变量 4 个
模式:宽松(未定义变量渲染为空)用户:用这套模板和数据生成项目周报正文
助手:调用
render_template,text传模板、data传 JSON
用户:严格一点,漏了变量就报错,别静默留空
助手:调用
render_template,加strict=true
用户:从 data.json 文件读数据渲染 tpl.txt
助手:调用
render_template,path=tpl.txt、data_path=data.json
validate_template
发布模板前做静态检查:语法(未闭合标签、未知标签、非法过滤器、非法变量名)、标签数量、在给定数据下未定义的变量、以及数据里未被引用的顶层键。语法或渲染期问题会以「检查报告」返回,而不是直接抛错中断。
模板来源:(内联模板);数据来源:(内联数据)
语法检查:通过(无未闭合标签、未知标签、非法过滤器)
标签统计:变量 4;if 1;each 1;注释 0
未定义变量:无
未使用的数据键:members、total用户:发布前帮我检查下这个模板有没有语法问题和漏变量
助手:调用
validate_template,text传模板、data传一份真实数据
用户:只查语法,不关心数据是否齐全
助手:调用
validate_template,只传text(不传 data,未定义变量会全量列出)
extract_vars
扫描模板,列出所有引用的变量路径(含 each 列表路径、if 条件路径)与使用的过滤器,并统计 if / each / 注释 数量。适合在写模板前先确认「需要准备哪些数据字段」。
模板来源:(内联模板)
标签统计:变量 4;if 1;each 1;注释 0
引用变量(4):note、project、u.name、u.role
使用的过滤器(1):default用户:我要写一个模板,先告诉我需要准备哪些数据字段
助手:调用
extract_vars,text传草稿模板,根据返回的变量清单准备数据
典型场景
- 批量生成:同一模板配多份数据,生成报告、邮件、公告、配置片段,不用每次手写。
- 配置注入:把
{{ 环境 }}、{{ 端口 }}之类的占位符填进部署脚本、连接串、yaml。 - 结构化文本:生成用户列表、工单清单,用
{% each %}循环展开,{% empty %}处理「暂无」。 - 文案分级:用
{% if %}按数值 / 状态走不同措辞,避免在代码里堆 if-else 拼字符串。 - 发布前质检:
validate_template先查出漏变量、未闭合标签、多余数据键,避免生成一半再发现。 - 数据契约先行:写模板前先
extract_vars,把需要的字段列给上游,减少返工。 - 脚本与注释:
{# … #}把说明留在模板里,渲染输出自动去干净。 - 只读安全:所有操作不写文件、不联网,模板里的内容只当字符串处理,不会被执行。
安全边界
- 只读:三个工具都不写入、不修改、不删除文件;测试含源文件 size/mtime 不变的只读性断言。
- 不访问网络:分词、校验、渲染全部在本地完成,无任何外部请求。
- 不执行代码:模板是纯文本 + 数据查找,不用
eval、不加载模块、不解析表达式函数;{% if %}只支持变量与简单比较,不支持任意代码。 - 二进制拒绝:读取模板或数据文件时检测 NUL 字节,疑似二进制文件直接拒绝。
- 多层上限:文件大小上限 51200 KB(默认 10240 KB)、输出字符上限 200000(默认 20000)。超限即截断或拒绝并显式提示。
- 不做任意外部调用:过滤器只做字符串 / 数值 / 集合的纯函数变换,不发起请求、不访问文件系统。
- 不脱敏:数据中的字段原样进入渲染结果。模板或数据里可能含密钥、路径、个人信息,分享前请先用
dsh-credential-scan检查。
其他插件集成说明
| 协作插件 | 集成方式 | |---|---| | dsh-json-toolkit | 先用 JSON 工具取到对象,再交给本插件渲染成文本 | | dsh-csv-inspect | 把 CSV 的列映射成 JSON 数据后,用模板生成逐行文本 | | dsh-text-stats | 渲染结果交给 text-stats 看长度、词频等统计 | | dsh-chart-ascii | 模板生成文字列表,chart-ascii 把其中的数值画成图 | | dsh-markdown-lint | 模板产出的 Markdown 文本再过一遍 markdown-lint 检查格式 | | dsh-credential-scan | 分享渲染结果前扫描是否混入密钥、令牌 | | dsh-config-merge | 从多个配置源合并数据后,用模板统一生成目标配置 |
版本说明
- 0.1.0(2026-10-05):初始版本。
render_template:{{ var }}点路径与过滤器、{% if %}条件、{% each %}循环(含{% empty %}、for.index/first/last)、{# 注释 #}、\{{转义、strict 模式validate_template:语法与标签结构检查、未定义变量检测、未使用数据键检测extract_vars:变量路径(含 each 列表、if 条件)与过滤器清单、标签统计- 过滤器:
upper/lower/trim/length/json/number/round/default/join/first/last - 自写分词 + 递归渲染,不做 eval,未闭合标签 / 未知标签 / 非法变量名 / 非法过滤器均显式报错
- 二进制拒绝、多层上限与截断提示
RoadMap
v0.2(计划)
- 增加
{% set %}:模板内定义中间变量,减少重复表达式 - 增加
{% include %}:引用片段模板,支持复用公共头尾 - 过滤器扩充:
replace、split、slice、date(格式化时间字段) - 输出可选 JSON 结构(变量清单、渲染耗时、每处未定义变量的位置)
v0.3(展望)
- 循环支持
{% each key,value in 对象 %},遍历对象键值对 - 增加「渲染预览」工具:高亮所有未定义变量位置,便于排查长模板
- 与
dsh-doc-outline联动,把模板渲染出的标题结构画成大纲 - 增加
linter风格建议:重复变量、可提取的子串等
FAQ
Q:为什么变量没渲染出来,直接变成空串?
默认(宽松模式)下未定义变量渲染为空串,因为输入数据里没有对应的键。可先用 extract_vars 看需要哪些字段,或用 validate_template 查出未定义变量,或在 render_template 加 strict=true 让它直接报错。
Q:{{ a.b.c }} 找不到会怎样?
逐级查找,任何一级为 null / undefined 就视为未定义。路径第一段会尝试从最内层 each 的循环变量解析,找不到再回退到根数据。
Q:{% if %} 支持哪些条件?
任意变量(真值:非零、非空串、非空数组、非 null)或形如 变量 == 值、变量 > 值 的比较,右侧支持数字、带引号的字符串、true / false。不支持函数调用、运算式、正则。
Q:{% each %} 里怎么拿循环变量?
{% each list as item %} 中可用 {{ item }} 或 {{ item.字段 }};缺省 as 时变量名默认为 item。另可用 {{ for.index }}(0 起)、{{ for.first }}、{{ for.last }}。
Q:列表是空的时候想显示「暂无」怎么处理?
用 {% each list as item %}{{ item }}{% empty %}暂无{% endeach %}。列表为空时只渲染 empty 分支;列表非空且没有 empty 标签则正常循环。
Q:过滤器参数要不要带引号?
带不带都可以:{{ n | round:1 }} 与 {{ s | default:"暂无" }} 都会去掉两端引号。default 的值含引号或特殊字符时建议带上引号避免歧义。
Q:\{{ 是什么?
模板里想输出字面 {{ 时用 \{{ 转义,渲染结果是 {{(不含反斜杠)。偶数个反斜杠时后一个为字面反斜杠,奇数个才视为转义。
Q:模板里能不能执行代码或调用函数?
不能。这是刻意的安全边界——解析器只认变量、条件、循环、注释和过滤器,内容当字符串处理,不做 eval。需要复杂逻辑请在数据里预先算好,或用专门的代码插件。
Q:未闭合标签会怎样?
分词或渲染时会抛错,validate_template 会把「缺 {% endif %} / {% endeach %}」等作为检查报告列出,不会生成一半的输出。
Q:数据里很多键模板没用上,会警告吗?
validate_template 会列出「未使用的数据键」。注意循环变量(item 等)和路径首段属于局部引用,不会被误判为数据键。
Q:输出很长会被截断吗?
超过 max_chars(默认 20000,上限 200000)时截断并提示总字符数;也可用 {% each %} 配 for.index 自己控制条数,或用工具端 limit 思路先缩窄输入。
源码与发布地址
- 源码:https://github.com/geeklei/dsh-plugins/tree/main/dsh-template-render
- npm:https://www.npmjs.com/package/dsh-template-render
许可证
MIT
