如何自动封装模板任意表达式以实现n8n自定义链式辅助方法调用
核心结论前置
不存在100%万无一失的正则实现方案,JS表达式属于上下文无关文法,正则文法的能力不足以覆盖所有嵌套、转义、特殊格式的合法表达式场景,更稳妥的方案是基于AST语法解析实现。
方案1:基于JS语法解析器的实现(推荐,全场景兼容)
这是唯一能覆盖所有合法JS表达式的方案,完全无视用户写的换行、空格、注释等随意格式,也不会误修改字符串、正则内部的内容。
实现步骤:
- 用正则
/{{([\s\S]+?)}}/g提取模板中所有双大括号包裹的表达式片段 - 引入轻量JS解析库(如acorn、meriyah)将表达式转为AST抽象语法树
- 遍历AST,识别所有「成员访问表达式(MemberExpression)」的左节点(即
x.y/x[y]中的x部分),排除已被_()包裹的节点、原生全局对象(如Math/console)后,给左节点套上_()包裹 - 将修改后的AST转回JS代码,替换回原模板的对应位置即可
配套的辅助函数实现
你的_辅助函数可以用Proxy实现,自动支持链式调用,不用额外处理多层属性访问:
// 自定义方法注册中心 const customMethods = new Map([ ['isEmail', val => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val)], ['isPrime', val => { /* 素数判断逻辑 */ }], ['hasField', (obj, field) => Object.prototype.hasOwnProperty.call(obj, field)] ]) function _(value) { // 避免重复包裹 if (value?.__isN8nProxy) return value // 基础类型包装为对象 const wrapped = Object(value) return new Proxy(wrapped, { get(target, prop) { // 优先返回自定义方法 if (customMethods.has(prop)) { return (...args) => _(customMethods.get(prop)(target.valueOf(), ...args)) } // 原生方法调用后自动包裹返回值 if (prop in target) { const orig = target[prop] if (typeof orig === 'function') { return (...args) => _(orig.apply(target, args)) } return _(orig) } return _(undefined) } }) } // 给代理加标记避免重复包裹 Object.defineProperty(_.prototype, '__isN8nProxy', { value: true })
方案2:有限场景的正则方案(仅适合简单场景)
如果你的用户不会写过于复杂的表达式,可以用正则覆盖绝大多数常规使用场景,注意该方案存在边界缺陷:
- 无法处理字符串/正则内部出现自定义方法名的场景,会误修改
- 嵌套过深的表达式可能匹配错误
- 表达式内的注释会干扰匹配
实现代码:
// 先把你要提供的所有自定义方法名列出来 const customMethodList = ['isEmail', 'isPrime', 'hasField', 'removeField'] // 匹配规则:忽略空白换行,匹配所有后面跟着自定义方法调用的前置表达式 const wrapRegex = new RegExp(`([\\s\\S]+?)(\\.\\s*(${customMethodList.join('|')})\\s*\\()`, 'g') function wrapExpression(expr) { return expr.replace(wrapRegex, (match, prefix, suffix) => { // 已经被_()包裹的就不用重复处理 if (/^_\s*\(/.test(prefix.trim())) return match return `_(${prefix})${suffix}` }) }
该方案可以正确处理你举的换行示例:
处理前:
{{ (5 + 2 ).isPrime () ? 'This is prime' : 42 }}
处理后:
{{ _((5 + 2 )).isPrime () ? 'This is prime' : 42 }}
内容的提问来源于stack exchange,提问作者Krynble
相关产品推荐
相关产品推荐

