Spintax 用于 JavaScript 和 TypeScript
spintax 引擎现已作为独立 npm 包发布:@spintax/core。零依赖,MIT 许可,不绑定任何框架——驱动 WordPress 插件的那套 parse / render / validate 原语,现在任何能跑 JavaScript 的地方都能用。
这是什么
@spintax/core 是一个开源、不绑定框架的 spintax 引擎,面向 JavaScript 和 TypeScript。它是 Spintax WordPress 插件的同伴项目——同一套语法的独立 TypeScript 实现,零 WordPress 依赖。一个引擎,多个表面。
- 运行时零依赖。在 Cloudflare Workers、Node 18+ 和浏览器里不加修改直接运行。
- ESM 优先,兼发 CJS。两套模块体系都附带
.d.ts类型。 - MIT 许可。WordPress 插件保持 GPL;MIT/Expat 与 GPL 兼容,两者干净共存。
- 一致性有机器验证——与 PHP 插件共享一套 golden corpus,不是承诺,是机器检查的闸门。
安装
npm install @spintax/core
快速上手
import { render, validate, extract } from '@spintax/core';
render('{Hello|Hi|Hey} %name%!', { context: { name: 'Ada' }, seed: 42 });
// → "Hi Ada!" (deterministic for a given seed; post-processed by default)
validate('{a|b'); // → [{ severity: 'error', code: 'bracket.unclosed', … }]
extract('%title% {?promo?Sale}'); // → { refs: ['title', 'promo'], sets: [], defs: [], includes: [] }
带上 seed,render 完全可复现;不带则每次调用都从模板空间里抽一个新的随机结果。
API
一个小而锋利的核心。每个函数都既接受原始 string,也接受已解析的 Ast(来自 parse)——调用方可以解析一次、反复使用。
| 函数 | 作用 |
|---|---|
parse(src) | 解析一次,得到不透明、带版本的 Ast 供复用——这是内存里的性能句柄,不是序列化格式。 |
render(input, opts?) | 渲染成一个字符串。宽容——坏掉的标记也绝不抛异常。外观后处理(空格、大小写、URL/邮箱防护)默认开启。 |
validate(input, opts?) | 返回诊断。有效 ⇔ 没有 severity:'error'。未解析的 %var% 是警告而非错误。复数结论随 locale 而定。 |
extract(input) | 列出变量引用 refs、#set 与 #def 名称和 #include 目标——为异步 include 的两阶段预取提供依据。 |
analyze(input, opts?) | extract + validate + 尽力而为的构件统计。给工具用的统计层。 |
neutralize(value) | 防护来自数据的不可信文本,使其不会被再次解释为 spintax 标记。文本级防护,不是 HTML 转义。 |
render 接受 context(变量表)、seed、locale(复数形式桶,例如 ru 对应三形态规则)、可选的宿主注入 includeResolver、postProcess 开关,以及限制嵌套与 #include 深度的 maxDepth。
还是你已经认识的那套语法
这是完整的 spintax 语法面——语法参考逐字适用。
| 构件 | 示例 | 含义 |
|---|---|---|
| 枚举 | {a|b|c} | 选一个(可嵌套:{a|{b|c}}) |
| 排列 | [a|b|c] | 取 N 个、打乱、拼接——分隔符可配置 |
| 变量 | %var% | 代入上下文中的值 |
| 局部 set | #set %v% = value | 定义变量(单行);是宏——每次引用重新抽取 |
| 局部 def | #def %v% = value | 形式相同,但每次渲染只解析一次、处处一致 |
| 条件 | {?VAR?then|else} | 按值分支(指南) |
| 复数 | {plural %n%: one|few|many} | 按 locale 做语法一致(指南) |
| Include | #include "slug-or-id" | 嵌入另一个模板(由宿主解析) |
| 注释 | /# … #/ | 渲染前剔除 |
与 WordPress 插件的一致性
“独立实现,但在关键处保持一致”是这个包的全部意义——而且是被强制执行的,不是被打算的。一套共享 golden corpus(语言无关的用例:(template, context, locale, seed) → expected)同时被 PHP 插件的测试套件和这个 TypeScript 套件消费。
- 确定性行为纳入一致性闸门:校验结论、复数形式、条件真值、
#set/#def解析和后处理流水线,在两个引擎里断言完全相同的输出。 - 随机选择不纳入。带 seed 的渲染在同一引擎内可复现;跨引擎的随机序列一致是有意为之的非目标。两边都给你合法的结果,只是不保证同一个。
它不是对 GPL PHP 代码的逐行移植——而是从行为契约加上这套语料库重新实现的,这正是它能干净地保持 MIT 的原因。
一个引擎,多个表面
这个包是整个生态共享的运行时——下面每个表面都是同一个公开 API 的消费者,从来不是私有分叉:
- 本站的演练场(/play/EN)在你的浏览器里客户端渲染。
- 一个 Cloudflare Worker 参考 API——通过 HTTP 校验和预览渲染。
- Telegram 机器人 @spintaxnetbot——贴进模板,直接在聊天里拿到校验结果和几个渲染变体。
因为这些示例只 import @spintax/core、别无其他,每一个都在证明这个 API 不用被污染也够用。Worker 和机器人能用,你的项目就能用。