Skip to content

安装

shell
npm install knap

Knap 以 ESM 优先,同时也提供 CommonJS 入口,并要求 Node.js 20 或更高版本。

快速开始

ts
import {
  createEngine,
  standardFilters,
  type TemplateVariables,
} from 'knap';

const engine = createEngine({ filters: standardFilters });

const variables: TemplateVariables = {
  title: '  An imported note  ',
  tags: ['reference', 'reading'],
};

const result = await engine.render(
  '# {{ title | trim }}\n\n{{ tags | list }}',
  { variables },
);

变量与解析器

每次渲染调用时传入变量。Knap 接受未知的应用取值,而不会强制套用 schema。

Knap 并不知道浏览器标签页、vault、选择器或模型是什么。应用可以把这些概念作为普通取值暴露出来,或按需解析它们。

如果某个取值不在变量对象中,宿主可以用 resolveVariable 加载它。本地取值始终优先。当自定义过滤器或解析器需要并非模板变量的宿主数据时,传入一个可选的泛型上下文。

ts
const result = await engine.render('{{ remoteValue | upper }}', {
  variables: {},
  context: { documentId: 'example' },
  resolveVariable: async (name, { context }) => {
    if (name === 'remoteValue') {
      return loadValue(context.documentId);
    }
    return undefined;
  },
});

渲染结果

render() 始终会返回一个包含结构化诊断信息的输出对象。

字段类型说明
outputstring渲染后的 Markdown。解析失败时为空。
errorsTemplateError[]致命的语法解析器、校验、解析器或过滤器错误。
warningsTemplateWarning[]由过滤器报告的非致命诊断信息。

错误包含稳定的 codemessagelinecolumn。警告还会标明报告它的过滤器,并在每次渲染中去重。

ts
if (result.errors.length === 0) {
  console.log(result.output);
}

const output = await engine.renderOrThrow(
  '{{ title | upper }}',
  { variables },
);

警告绝不会让 renderOrThrow() 抛出异常。当异常比检查结果更契合宿主应用时,就用它。

引擎方法

方法说明
createEngine({ filters })用不可变的过滤器注册表创建引擎。
engine.render(template, input, options?)异步渲染,并返回输出、错误和警告。
engine.renderOrThrow(template, input, options?)返回输出,或抛出 TemplateRenderError
engine.parse(template)返回 AST 与语法解析器诊断信息。
engine.validate(templateOrAst)校验语法以及过滤器名称或参数。

在渲染选项中设置 { trimOutput: false },以保留模板周围的空白。

执行模型

Knap 把模板解析为 AST,并在不使用 eval、也不执行任意 JavaScript 的情况下解释它们。应用控制每个引擎可用的变量、异步解析器和自定义过滤器。

渲染限制

引擎默认应用有限的限制。可以在构造时配置,也可以在渲染选项中传入 limits 对象,为单次渲染覆盖默认限制:

ts
const engine = createEngine({
  filters: standardFilters,
  allowRegex: false,
  limits: {
    maxTemplateLength: 100_000,
    maxOutputLength: 100_000,
    maxValueLength: 1_000_000,
    maxOperations: 50_000,
    maxDepth: 50,
  },
});

allowRegex: false 会让 split 把分隔符按字面处理,并拒绝 replace 中的正则搜索。字面替换仍然可用。为兼容起见,正则匹配默认保持启用。在接受不受信任的模板或正则输入时,应在可终止的 worker 或进程中运行渲染,并设置挂钟超时;同线程的 Promise 超时无法中断原生正则匹配。自定义过滤器和解析器属于受信任的宿主代码,如果它们可能阻塞或分配无界数据,同样需要隔离。限制并不是 JavaScript 沙箱。

导出的 defaultRenderLimits 为:1,000,000 个模板字符、5,000,000 个输出字符、每个取值 5,000,000 个字符、100,000 次工作操作、嵌套预算 100。工作包括表达式求值、迭代和取值遍历。长度使用 JavaScript 字符串单位;集合取值检查包含键和结构开销。限制必须是正的安全整数;maxDepth 不能超过 256。超出限制会返回 LIMIT_EXCEEDED,且输出为空。renderOrThrow 照常抛出 TemplateRenderErrorparsevalidate 也会报告嵌套和模板大小的限制。

传入纯数据,而不是带访问器或自定义序列化的对象。属性查找只读取自有数据属性;继承来的取值和 getter 不会被解析。HTML 过滤器会操作标记,但不会对其做净化。

编辑器工具

这些更低层的导出让编辑器可以只做一次词法分析、检查 AST,并独立校验变量或过滤器。

ts
import {
  parse,
  standardFilterMetadata,
  validateFilters,
  validateVariables,
} from 'knap';

const parsed = parse(template);
const filterErrors = validateFilters(parsed.ast, standardFilterMetadata);
const variableNames = validateVariables(parsed.ast);

当宿主需要在完整渲染之外使用 Knap 过滤器语法时,applyFiltersWithRegistry() 会应用同步的过滤器链。

过滤器回调接收的第一个参数是兼容用的字符串取值。对于需要区分集合与字面 JSON 文本的过滤器,FilterContext.rawValue 保存原始的有类型取值。

包导出

导入包含
knap引擎、语法解析器、词法分析器、标准过滤器、诊断信息和公开类型。
knap/html依赖 DOM 的 html_to_jsonremove_html 过滤器。

显示输出

Knap 生成 Markdown,其中包含模板或变量提供的任何 HTML。把它作为文本渲染,或使用在把结果插入页面之前会净化 HTML、校验链接协议的 Markdown 渲染器。strip_tagsremove_html 这类 HTML 操作过滤器并非净化器。linkimage 过滤器会转义字面标签和目的地,并省略 javascript:vbscript:data: 目的地;相对链接和 obsidian: 这类应用协议仍然受支持。宿主在显示结果时应应用自己的协议策略。