index.ts
  1  import Helper from "./Helper"
  2  import Handlebars from "handlebars"
  3  import * as externalHandlebars from "./external"
  4  import { processJS } from "./javascript"
  5  import {
  6    HelperFunctionNames,
  7    HelperFunctionBuiltin,
  8    LITERAL_MARKER,
  9  } from "./constants"
 10  
 11  export { getJsHelperList } from "./list"
 12  
 13  const HTML_SWAPS = {
 14    "<": "&lt;",
 15    ">": "&gt;",
 16  }
 17  
 18  function isObject(value: string | any[]) {
 19    if (value == null || typeof value !== "object") {
 20      return false
 21    }
 22    return (
 23      value.toString() === "[object Object]" ||
 24      (value.length > 0 && typeof value[0] === "object")
 25    )
 26  }
 27  
 28  export const HELPERS = [
 29    // external helpers
 30    new Helper(HelperFunctionNames.OBJECT, (value: any) => {
 31      return new Handlebars.SafeString(JSON.stringify(value))
 32    }),
 33    // javascript helper
 34    new Helper(HelperFunctionNames.JS, processJS, false),
 35    new Helper(HelperFunctionNames.DECODE_ID, (_id: string | { _id: string }) => {
 36      if (!_id) {
 37        return []
 38      }
 39      // have to replace on the way back as we swapped out the double quotes
 40      // when encoding, but JSON can't handle the single quotes
 41      const id = typeof _id === "string" ? _id : _id._id
 42      const decoded: string = decodeURIComponent(id).replace(/'/g, '"')
 43      try {
 44        const parsed = JSON.parse(decoded)
 45        return Array.isArray(parsed) ? parsed : [parsed]
 46      } catch (err) {
 47        // wasn't json - likely was handlebars for a many to many
 48        return [_id]
 49      }
 50    }),
 51    // this help is applied to all statements
 52    new Helper(
 53      HelperFunctionNames.ALL,
 54      (value: string, inputs: { __opts: any }) => {
 55        const { __opts } = inputs
 56        if (isObject(value)) {
 57          return new Handlebars.SafeString(JSON.stringify(value))
 58        }
 59        // null/undefined values produce bad results
 60        if (__opts && __opts.onlyFound && value == null) {
 61          return __opts.input
 62        }
 63        if (value == null || typeof value !== "string") {
 64          return value == null ? "" : value
 65        }
 66        // TODO: check, this should always be false
 67        if (value && (value as any).string) {
 68          value = (value as any).string
 69        }
 70        let text: any = value
 71        if (__opts && __opts.escapeNewlines) {
 72          text = value.replace(/\n/g, "\\n")
 73        }
 74        text = new Handlebars.SafeString(text.replace(/&amp;/g, "&"))
 75        if (text == null || typeof text !== "string") {
 76          return text
 77        }
 78        return text.replace(/[<>]/g, (tag: string) => {
 79          return HTML_SWAPS[tag as keyof typeof HTML_SWAPS] || tag
 80        })
 81      }
 82    ),
 83    // adds a note for post-processor
 84    new Helper(HelperFunctionNames.LITERAL, (value: any) => {
 85      if (value === undefined) {
 86        return ""
 87      }
 88      const type = typeof value
 89      const outputVal = type === "object" ? JSON.stringify(value) : value
 90      return `{{${LITERAL_MARKER} ${type}-${outputVal}}}`
 91    }),
 92  ]
 93  
 94  export function HelperNames() {
 95    return Object.values(HelperFunctionNames).concat(
 96      HelperFunctionBuiltin,
 97      externalHandlebars.externalHelperNames
 98    )
 99  }
100  
101  export function registerMinimum(handlebars: typeof Handlebars) {
102    for (let helper of HELPERS) {
103      helper.register(handlebars)
104    }
105  }
106  
107  export function registerAll(handlebars: typeof Handlebars) {
108    registerMinimum(handlebars)
109    // register imported helpers
110    externalHandlebars.registerAll(handlebars)
111  }
112  
113  export function unregisterAll(handlebars: any) {
114    for (let helper of HELPERS) {
115      helper.unregister(handlebars)
116    }
117    // unregister all imported helpers
118    externalHandlebars.unregisterAll(handlebars)
119  }