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 "<": "<", 15 ">": ">", 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(/&/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 }