使用Handlebars生成YAML时如何避免引号被转义为"?
解决Handlebars生成YAML时双引号被过度转义的问题
问题背景
使用Handlebars编译模板生成Argo CD ApplicationSet的YAML文件时,原本的双引号"被转义成",连=也被转成=,导致生成的YAML无法正常使用。不想采用二次替换这种临时方案,需要更高效的解决办法。
核心原因
Handlebars默认会自动转义HTML实体(比如把"转成",嵌套渲染时会进一步变成"),这是因为它最初是为HTML场景设计的。但YAML不需要这种转义,因此要禁用该自动转义行为。
解决方案
方案1:使用三重大括号输出不转义内容
在主模板中,将{{templatePlaceholder}}改为{{{templatePlaceholder}}},三重大括号会让Handlebars直接输出变量的原始内容,不会转义其中的任何字符。
修改后的主模板:
apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: name: {{serviceName}} namespace: argocd-prod spec: generators: - list: {{#application}} - namespace: {{namespace}} environment: {{env}} region: {{region}} path: {{path}} clusterurl: {{clusterUrl}} {{/application}} {{! BEGIN template block }} {{{templatePlaceholder}}} {{! END template block }}
该方案无需修改JS代码,仅调整主模板的占位符语法即可解决转义问题。
方案2:编译模板时开启全局不转义选项
编译Handlebars模板时,添加noEscape: true参数,全局禁用自动转义。注意:此选项会让所有{{variable}}的输出都不转义,需确保所有变量内容安全(无恶意HTML字符)后再使用。
修改JS代码中的编译逻辑:
const compiledTemplate = Handlebars.compile(yamlData, { noEscape: true });
方案3:用Handlebars局部模板重构(更规范)
将template块单独做成Handlebars模板文件,注册为局部模板,避免字符串嵌套替换的问题,更符合Handlebars的最佳实践。
- 创建局部模板文件
templates/appset-template-partial.hbs:
template: metadata: name: "{{environment}}-{{region}}-{{namespace}}-{{serviceName}}" labels: service: {{serviceName}}-{{environment}} tribe: {{tribe}} squad: {{squad}} environment: "{{environment}}" region: "{{region}}" spec: project: {{squad}} source: targetRevision: HEAD path: "{{path}}" destination: server: "{{clusterurl}}" namespace: "{{namespace}}" syncPolicy: automated: prune: true selfHeal: true allowEmpty: true syncOptions: - CreateNamespace=true
- 修改JS代码,注册并使用局部模板:
import 'core-js/features/string/replace-all'; import { ApplicationSet, Owner } from './interfaces/types'; import * as fs from 'fs'; import * as Handlebars from 'handlebars'; const yamlTemplate = 'templates/argocd-appset.yaml'; // 注册局部模板 const partialTemplate = fs.readFileSync('templates/appset-template-partial.hbs', 'utf8'); Handlebars.registerPartial('appsetTemplate', partialTemplate); export function generateApp(appset: ApplicationSet, owner: Owner): string { const yamlData = fs.readFileSync(yamlTemplate, 'utf8'); const compiledTemplate = Handlebars.compile(yamlData); // 合并所有数据,无需单独处理template块 const merged = {...appset,...owner}; const renderedYAML = compiledTemplate(merged); return renderedYAML; }
- 修改主模板,调用局部模板:
apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: name: {{serviceName}} namespace: argocd-prod spec: generators: - list: {{#application}} - namespace: {{namespace}} environment: {{env}} region: {{region}} path: {{path}} clusterurl: {{clusterUrl}} {{/application}} {{! BEGIN template block }} {{> appsetTemplate}} {{! END template block }}
该方案彻底避免了字符串嵌套替换的问题,代码结构更清晰,维护性更强。
内容的提问来源于stack exchange,提问作者Vini
相关产品推荐
相关产品推荐

