All files / utils StringTemplate.ts

100% Statements 18/18
100% Branches 21/21
100% Functions 7/7
100% Lines 18/18

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 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82                                                              21x 21x 21x 21x 13x 13x         3x                   4x         7x       21x 12x   9x 4x     9x 4x   5x     21x         5x      
import { toSingleLineString } from './toSingleLineString'
 
export interface StringTemplateRenderOptions {
  /** 是否启用代码渲染,需环境支持 eval */
  code?: boolean
  /** 是否仅代码渲染 */
  onlyCode?: boolean
}
 
/**
 * 字符串模板。
 */
export class StringTemplate {
  /**
   * 渲染字符串模板。语法:
   *
   * - 用 `{key}` 直接替换;
   * - 用 `{key#10}` 直接替换,取前10个字符省略;
   * - 用 `{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 onlyCode = !!options?.onlyCode
    const enableCode = onlyCode || !!options?.code
    const keys = Object.keys(data)
    if (!onlyCode) {
      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}(?:#(\\d+))?\\}`, 'g'),
                (_, len) =>
                  len ? toSingleLineString(String(data[key]), +len) : data[key],
              )
            : template.replace(
                new RegExp(`{${key}(?:#(\\d+))?\\}`, 'g'),
                (_, len) =>
                  len ? toSingleLineString(String(data[key]), +len) : data[key],
              )
      }
    }
    if (enableCode) {
      template = template.replace(/\{\{(.+?)\}\}/g, (_, code) => {
        // 需在 eval 里函数两边加上括号才能返回函数
        const res = eval(`(function (data) {
          ${keys.map(key => `var ${key} = data["${key}"];`).join('')}
          return ${code};
        })`)(data)
        if (res == null || res === true || res === false) {
          return ''
        }
        return res
      })
    }
    return template
  }
 
  /** 判断是否有代码块 */
  static hasCodeBlock(template: string): boolean {
    return /\{\{(.+?)\}\}/.test(template)
  }
}