diff --git a/packages/string-templates/src/helpers/Helper.ts b/packages/string-templates/src/helpers/Helper.ts index e254b2086d..a1722ac2c8 100644 --- a/packages/string-templates/src/helpers/Helper.ts +++ b/packages/string-templates/src/helpers/Helper.ts @@ -1,31 +1,34 @@ export default class Helper { - private name - private fn - private useValueFallback + private name: any + private fn: any + private useValueFallback: boolean - constructor(name, fn, useValueFallback = true) { + constructor(name: string, fn: any, useValueFallback = true) { this.name = name this.fn = fn this.useValueFallback = useValueFallback } - register(handlebars) { + register(handlebars: typeof Handlebars) { // wrap the function so that no helper can cause handlebars to break - handlebars.registerHelper(this.name, (value, info) => { - let context = {} - if (info && info.data && info.data.root) { - context = info.data.root + handlebars.registerHelper( + this.name, + (value: any, info: { data: { root: {} } }) => { + let context = {} + if (info && info.data && info.data.root) { + context = info.data.root + } + const result = this.fn(value, context) + if (result == null) { + return this.useValueFallback ? value : null + } else { + return result + } } - const result = this.fn(value, context) - if (result == null) { - return this.useValueFallback ? value : null - } else { - return result - } - }) + ) } - unregister(handlebars) { + unregister(handlebars: { unregisterHelper: any }) { handlebars.unregisterHelper(this.name) } } diff --git a/packages/string-templates/src/helpers/external.ts b/packages/string-templates/src/helpers/external.ts index 7e849c8fc3..efb390ff8b 100644 --- a/packages/string-templates/src/helpers/external.ts +++ b/packages/string-templates/src/helpers/external.ts @@ -1,4 +1,4 @@ -import helpers from "@budibase/handlebars-helpers" +const helpers = require("@budibase/handlebars-helpers") import { date, duration } from "./date" import { HelperFunctionBuiltin } from "./constants" @@ -27,7 +27,7 @@ const ADDED_HELPERS = { export const externalCollections = EXTERNAL_FUNCTION_COLLECTIONS export const addedHelpers = ADDED_HELPERS -export function registerAll(handlebars) { +export function registerAll(handlebars: typeof Handlebars) { for (let [name, helper] of Object.entries(ADDED_HELPERS)) { handlebars.registerHelper(name, helper) } @@ -55,7 +55,7 @@ export function registerAll(handlebars) { externalHelperNames = externalNames.concat(Object.keys(ADDED_HELPERS)) } -export function unregisterAll(handlebars) { +export function unregisterAll(handlebars: typeof Handlebars) { for (let name of Object.keys(ADDED_HELPERS)) { handlebars.unregisterHelper(name) } @@ -65,4 +65,4 @@ export function unregisterAll(handlebars) { externalHelperNames = [] } -export let externalHelperNames = [] +export let externalHelperNames: any[] = [] diff --git a/packages/string-templates/src/helpers/index.ts b/packages/string-templates/src/helpers/index.ts index b375426c0b..e95ac9d2fd 100644 --- a/packages/string-templates/src/helpers/index.ts +++ b/packages/string-templates/src/helpers/index.ts @@ -15,7 +15,7 @@ const HTML_SWAPS = { ">": ">", } -function isObject(value) { +function isObject(value: string | any[]) { if (value == null || typeof value !== "object") { return false } @@ -27,42 +27,45 @@ function isObject(value) { const HELPERS = [ // external helpers - new Helper(HelperFunctionNames.OBJECT, value => { + new Helper(HelperFunctionNames.OBJECT, (value: any) => { return new SafeString(JSON.stringify(value)) }), // javascript helper new Helper(HelperFunctionNames.JS, processJS, false), // this help is applied to all statements - new Helper(HelperFunctionNames.ALL, (value, inputs) => { - const { __opts } = inputs - if (isObject(value)) { - return new SafeString(JSON.stringify(value)) + new Helper( + HelperFunctionNames.ALL, + (value: string, inputs: { __opts: any }) => { + const { __opts } = inputs + if (isObject(value)) { + return new SafeString(JSON.stringify(value)) + } + // null/undefined values produce bad results + if (__opts && __opts.onlyFound && value == null) { + return __opts.input + } + if (value == null || typeof value !== "string") { + return value == null ? "" : value + } + // TODO: check, this should always be false + if (value && (value as any).string) { + value = (value as any).string + } + let text = value + if (__opts && __opts.escapeNewlines) { + text = value.replace(/\n/g, "\\n") + } + text = new SafeString(text.replace(/&/g, "&")).toString() + if (text == null || typeof text !== "string") { + return text + } + return text.replace(/[<>]/g, (tag: string) => { + return HTML_SWAPS[tag as keyof typeof HTML_SWAPS] || tag + }) } - // null/undefined values produce bad results - if (__opts && __opts.onlyFound && value == null) { - return __opts.input - } - if (value == null || typeof value !== "string") { - return value == null ? "" : value - } - // TODO: check, this should always be false - if (value && (value as any).string) { - value = (value as any).string - } - let text = value - if (__opts && __opts.escapeNewlines) { - text = value.replace(/\n/g, "\\n") - } - text = new SafeString(text.replace(/&/g, "&")) - if (text == null || typeof text !== "string") { - return text - } - return text.replace(/[<>]/g, tag => { - return HTML_SWAPS[tag] || tag - }) - }), + ), // adds a note for post-processor - new Helper(HelperFunctionNames.LITERAL, value => { + new Helper(HelperFunctionNames.LITERAL, (value: any) => { if (value === undefined) { return "" } @@ -79,19 +82,19 @@ export function HelperNames() { ) } -export function registerMinimum(handlebars) { +export function registerMinimum(handlebars: typeof Handlebars) { for (let helper of HELPERS) { helper.register(handlebars) } } -export function registerAll(handlebars) { +export function registerAll(handlebars: typeof Handlebars) { registerMinimum(handlebars) // register imported helpers externalHandlebars.registerAll(handlebars) } -export function unregisterAll(handlebars) { +export function unregisterAll(handlebars: any) { for (let helper of HELPERS) { helper.unregister(handlebars) } diff --git a/packages/string-templates/src/index.ts b/packages/string-templates/src/index.ts index 3796d8f155..0ec8fdd425 100644 --- a/packages/string-templates/src/index.ts +++ b/packages/string-templates/src/index.ts @@ -46,8 +46,8 @@ function testObject(object: any) { /** * Creates a HBS template function for a given string, and optionally caches it. */ -let templateCache = {} -function createTemplate(string: string, opts: ProcessOptions) { +const templateCache: Record> = {} +function createTemplate(string: string, opts?: ProcessOptions) { opts = { ...defaultOpts, ...opts } // Finalising adds a helper, can't do this with no helpers @@ -87,7 +87,7 @@ export async function processObject( object: { [x: string]: any }, context: object, opts?: { noHelpers?: boolean; escapeNewlines?: boolean; onlyFound?: boolean } -) { +): Promise> { testObject(object) for (let key of Object.keys(object || {})) { if (object[key] != null) { @@ -114,7 +114,7 @@ export async function processString( string: string, context: object, opts?: ProcessOptions -) { +): Promise { // TODO: carry out any async calls before carrying out async call return processStringSync(string, context, opts) } @@ -132,7 +132,7 @@ export function processObjectSync( object: { [x: string]: any }, context: any, opts: any -) { +): object | Array { testObject(object) for (let key of Object.keys(object || {})) { let val = object[key] @@ -157,7 +157,7 @@ export function processStringSync( string: string, context: object, opts?: ProcessOptions -) { +): string { // Take a copy of input in case of error const input = string if (typeof string !== "string") { @@ -220,7 +220,7 @@ export function disableEscaping(string: string) { * @param {string} property The property which is to be wrapped. * @returns {string} The wrapped property ready to be added to a templating string. */ -export function makePropSafe(property: any) { +export function makePropSafe(property: any): string { return `[${property}]`.replace("[[", "[").replace("]]", "]") } @@ -230,7 +230,7 @@ export function makePropSafe(property: any) { * @param [opts] optional - specify some options for processing. * @returns {boolean} Whether or not the input string is valid. */ -export function isValid(string: any, opts?: any) { +export function isValid(string: any, opts?: any): boolean { const validCases = [ "string", "number", @@ -251,7 +251,7 @@ export function isValid(string: any, opts?: any) { }) template(context) return true - } catch (err) { + } catch (err: any) { const msg = err && err.message ? err.message : err if (!msg) { return false @@ -281,7 +281,7 @@ export function getManifest() { * @param handlebars the HBS expression to check * @returns {boolean} whether the expression is JS or not */ -export function isJSBinding(handlebars: any) { +export function isJSBinding(handlebars: any): boolean { return decodeJSBinding(handlebars) != null } @@ -290,7 +290,7 @@ export function isJSBinding(handlebars: any) { * @param javascript the JS code to encode * @returns {string} the JS HBS expression */ -export function encodeJSBinding(javascript: string) { +export function encodeJSBinding(javascript: string): string { return `{{ js "${btoa(javascript)}" }}` } @@ -299,7 +299,7 @@ export function encodeJSBinding(javascript: string) { * @param handlebars the JS HBS expression * @returns {string|null} the raw JS code */ -export function decodeJSBinding(handlebars: string) { +export function decodeJSBinding(handlebars: string): string | null { if (!handlebars || typeof handlebars !== "string") { return null } @@ -324,7 +324,7 @@ export function decodeJSBinding(handlebars: string) { * @param {string[]} strings The strings to look for. * @returns {boolean} Will return true if all strings found in HBS statement. */ -export function doesContainStrings(template: string, strings: any[]) { +export function doesContainStrings(template: string, strings: any[]): boolean { let regexp = new RegExp(FIND_HBS_REGEX) let matches = template.match(regexp) if (matches == null) { @@ -333,7 +333,7 @@ export function doesContainStrings(template: string, strings: any[]) { for (let match of matches) { let hbs = match if (isJSBinding(match)) { - hbs = decodeJSBinding(match) + hbs = decodeJSBinding(match)! } let allFound = true for (let string of strings) { @@ -354,7 +354,7 @@ export function doesContainStrings(template: string, strings: any[]) { * @param {string} string The string to search within. * @return {string[]} The found HBS blocks. */ -export function findHBSBlocks(string: string) { +export function findHBSBlocks(string: string): string[] { if (!string || typeof string !== "string") { return [] } @@ -375,7 +375,7 @@ export function findHBSBlocks(string: string) { * @param {string} string The word or sentence to search for. * @returns {boolean} The this return true if the string is found, false if not. */ -export function doesContainString(template: any, string: any) { +export function doesContainString(template: any, string: any): boolean { return doesContainStrings(template, [string]) } @@ -383,7 +383,7 @@ export function convertToJS(hbs: string) { const blocks = findHBSBlocks(hbs) let js = "return `", prevBlock: string | null = null - const variables = {} + const variables: Record = {} if (blocks.length === 0) { js += hbs }