All files / utils StringTemplate.ts

100% Statements 9/9
100% Branches 8/8
100% Functions 3/3
100% Lines 9/9

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58                                                    8x 8x 8x 8x         3x                   8x 3x 2x               8x      
export interface StringTemplateRenderOptions {
  /** 是否启用代码渲染,需环境支持 eval */
  code?: boolean
}
 
/**
 * 字符串模板。
 */
export class StringTemplate {
  /**
   * 渲染字符串模板。语法:
   *
   * - 用 `{key}` 直接替换;
   * - 用 `{key:param1,param2}` 执行函数替换;
   * - 用 `{{key==='test'?'hi':'hello'}}` 执行代码替换(内部使用 eval 实现,需开启选项里的 `code` 参数)。
   *
   * @param template 要渲染的模板
   * @param data 渲染数据
   * @param options 渲染选项
   * @returns 返回渲染后字符串
   */
  static render(
    template: string,
    data: Record<string, any>,
    options?: StringTemplateRenderOptions,
  ) {
    const enableCode = !!options?.code
    const keys = Object.keys(data)
    for (const key of keys) {
      template =
        typeof data[key] === 'function'
          ? template.replace(
              new RegExp(`\\{${key}(:.+?)?\\}`, 'g'),
              (_, params: string) => {
                return data[key].call(
                  null,
                  ...(params ? params.substring(1) : '').split(','),
                )
              },
            )
          : enableCode
          ? template.replace(new RegExp(`(?<!\\$)\\{${key}\\}`, 'g'), data[key])
          : template.replaceAll(`{${key}}`, data[key])
    }
    if (enableCode) {
      template = template.replace(/\{\{(.+?)\}\}/g, (_, code) => {
        return eval(`
          (() => {
            const {${keys.join(',')}} = ${JSON.stringify(data)};
            return ${code};
          })()
        `)
      })
    }
    return template
  }
}