diff --git a/packages/builder/package.json b/packages/builder/package.json index 96e328ec42..398118eb5c 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -76,7 +76,6 @@ "deepmerge": "^4.2.2", "fast-sort": "^2.2.0", "lodash": "^4.17.13", - "mustache": "^4.0.1", "posthog-js": "1.4.5", "remixicon": "^2.5.0", "shortid": "^2.2.15", diff --git a/packages/builder/src/builderStore/replaceBindings.js b/packages/builder/src/builderStore/replaceBindings.js index 0bf9f485c9..3afda0c658 100644 --- a/packages/builder/src/builderStore/replaceBindings.js +++ b/packages/builder/src/builderStore/replaceBindings.js @@ -1,8 +1,8 @@ -export const CAPTURE_VAR_INSIDE_MUSTACHE = /{{([^}]+)}}/g +export const CAPTURE_VAR_INSIDE_TEMPLATE = /{{([^}]+)}}/g export function readableToRuntimeBinding(bindableProperties, textWithBindings) { - // Find all instances of mustasche - const boundValues = textWithBindings.match(CAPTURE_VAR_INSIDE_MUSTACHE) + // Find all instances of template strings + const boundValues = textWithBindings.match(CAPTURE_VAR_INSIDE_TEMPLATE) let result = textWithBindings // Replace readableBindings with runtimeBindings @@ -22,7 +22,7 @@ export function runtimeToReadableBinding(bindableProperties, textWithBindings) { let temp = textWithBindings const boundValues = (typeof textWithBindings === "string" && - textWithBindings.match(CAPTURE_VAR_INSIDE_MUSTACHE)) || + textWithBindings.match(CAPTURE_VAR_INSIDE_TEMPLATE)) || [] // Replace runtimeBindings with readableBindings: diff --git a/packages/builder/src/builderStore/uuid.js b/packages/builder/src/builderStore/uuid.js index 5dbd9ccdbd..149da83c68 100644 --- a/packages/builder/src/builderStore/uuid.js +++ b/packages/builder/src/builderStore/uuid.js @@ -1,6 +1,6 @@ export function uuid() { // always want to make this start with a letter, as this makes it - // easier to use with mustache bindings in the client + // easier to use with template string bindings in the client return "cxxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace(/[xy]/g, c => { const r = (Math.random() * 16) | 0, v = c == "x" ? r : (r & 0x3) | 0x8 diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/AutomationBlockTagline.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/AutomationBlockTagline.svelte index b40a83ad75..b63cea4f9d 100644 --- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/AutomationBlockTagline.svelte +++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/AutomationBlockTagline.svelte @@ -1,5 +1,5 @@ diff --git a/packages/builder/yarn.lock b/packages/builder/yarn.lock index f2f7363669..a0e2b8be9e 100644 --- a/packages/builder/yarn.lock +++ b/packages/builder/yarn.lock @@ -5324,11 +5324,6 @@ ms@2.1.2, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -mustache@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.0.1.tgz#d99beb031701ad433338e7ea65e0489416c854a2" - integrity sha512-yL5VE97+OXn4+Er3THSmTdCFCtx5hHWzrolvH+JObZnUYwuaG7XV+Ch4fR2cIrcYI0tFHxS7iyFYl14bW8y2sA== - nan@^2.12.1: version "2.14.2" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" diff --git a/packages/client/package.json b/packages/client/package.json index c7f04e2c6d..607cb55f71 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -11,7 +11,6 @@ "dependencies": { "@budibase/string-templates": "^0.5.3", "deep-equal": "^2.0.1", - "mustache": "^4.0.1", "regexparam": "^1.3.0", "svelte-spa-router": "^3.0.5" }, diff --git a/packages/client/src/utils/enrichDataBinding.js b/packages/client/src/utils/enrichDataBinding.js index 5de6b31a89..0a8ea0092b 100644 --- a/packages/client/src/utils/enrichDataBinding.js +++ b/packages/client/src/utils/enrichDataBinding.js @@ -1,23 +1,7 @@ -import mustache from "mustache" +import { processString } from "@budibase/string-templates" -// this is a much more liberal version of mustache's escape function -// ...just ignoring < and > to prevent tags from user input -// original version here https://github.com/janl/mustache.js/blob/4b7908f5c9fec469a11cfaed2f2bed23c84e1c5c/mustache.js#L78 -const entityMap = { - "<": "<", - ">": ">", -} -mustache.escape = text => { - if (text == null || typeof text !== "string") { - return text - } - return text.replace(/[<>]/g, function fromEntityMap(s) { - return entityMap[s] || s - }) -} - -// Regex to test inputs with to see if they are likely candidates for mustache -const looksLikeMustache = /{{.*}}/ +// Regex to test inputs with to see if they are likely candidates for template strings +const looksLikeTemplate = /{{.*}}/ /** * Enriches a given input with a row from the database. @@ -27,11 +11,11 @@ export const enrichDataBinding = (input, context) => { if (!input || typeof input !== "string") { return input } - // Do a fast regex check if this looks like a mustache string - if (!looksLikeMustache.test(input)) { + // Do a fast regex check if this looks like a template string + if (!looksLikeTemplate.test(input)) { return input } - return mustache.render(input, context) + return processString(input, context) } /** diff --git a/packages/client/yarn.lock b/packages/client/yarn.lock index e90defb427..9ea75db3b1 100644 --- a/packages/client/yarn.lock +++ b/packages/client/yarn.lock @@ -1362,11 +1362,6 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -mustache@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.0.1.tgz#d99beb031701ad433338e7ea65e0489416c854a2" - integrity sha512-yL5VE97+OXn4+Er3THSmTdCFCtx5hHWzrolvH+JObZnUYwuaG7XV+Ch4fR2cIrcYI0tFHxS7iyFYl14bW8y2sA== - nwsapi@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index e9c9140bfb..b72c09f88d 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -5,7 +5,6 @@ "main": "dist/bundle.js", "module": "dist/bundle.js", "license": "AGPL-3.0", - "private": true, "types": "dist/index.d.ts", "scripts": { "build": "rollup -c", diff --git a/packages/string-templates/src/helpers/index.js b/packages/string-templates/src/helpers/index.js index 1e305924a8..cbef0fb473 100644 --- a/packages/string-templates/src/helpers/index.js +++ b/packages/string-templates/src/helpers/index.js @@ -1,10 +1,26 @@ const Helper = require("./Helper") const { SafeString } = require("handlebars") +const HTML_SWAPS = { + "<": "<", + ">": ">", +} + const HELPERS = [ + // external helpers new Helper("object", value => { return new SafeString(JSON.stringify(value)) }), + // this help is applied to all statements + new Helper("all", value => { + let text = unescape(value).replace(/&/g, '&'); + if (text == null || typeof text !== "string") { + return text + } + return text.replace(/[<>]/g, tag => { + return HTML_SWAPS[tag] || tag + }) + }) ] module.exports.registerAll = handlebars => { diff --git a/packages/string-templates/src/index.js b/packages/string-templates/src/index.js index 369e383d55..4ef7cf5b3b 100644 --- a/packages/string-templates/src/index.js +++ b/packages/string-templates/src/index.js @@ -2,6 +2,7 @@ const handlebars = require("handlebars") const { registerAll } = require("./helpers/index") const HBS_CLEANING_REGEX = /{{[^}}]*}}/g +const FIND_HBS_REGEX = /{{.*}}/ const hbsInstance = handlebars.create() registerAll(hbsInstance) @@ -22,6 +23,12 @@ function attemptToCorrectError(string) { * @returns {string} The string that was input with cleaned up handlebars statements as required. */ function cleanHandlebars(string) { + // TODO: handle these types of statement + // every statement must have the "all" helper added e.g. + // {{ person }} => {{ html person }} + // escaping strings must be handled as such: + // {{ person name }} => {{ [person name] }} + // {{ obj.person name }} => {{ obj.[person name] }} let charToReplace = { "[": ".", "]": "", diff --git a/packages/string-templates/test/escapes.spec.js b/packages/string-templates/test/escapes.spec.js new file mode 100644 index 0000000000..acb852a96b --- /dev/null +++ b/packages/string-templates/test/escapes.spec.js @@ -0,0 +1,21 @@ +const { + processString, +} = require("../src/index") + +describe("Handling context properties with spaces in their name", () => { + it("should be able to handle a property with a space in its name", () => { + const output = processString("hello my name is {{ person name }}", { + "person name": "Mike", + }) + expect(output).toBe("hello my name is Mike") + }) + + it("should be able to handle an object with layers that requires escaping", () => { + const output = processString("testcase {{ testing.test case }}", { + testing: { + "test case": 1 + } + }) + expect(output).toBe("testcase 1") + }) +}) \ No newline at end of file diff --git a/packages/string-templates/test/helpers.spec.js b/packages/string-templates/test/helpers.spec.js new file mode 100644 index 0000000000..867b7a85f7 --- /dev/null +++ b/packages/string-templates/test/helpers.spec.js @@ -0,0 +1,12 @@ +const { + processString, +} = require("../src/index") + +describe("test the custom helpers we have applied", () => { + it("should be able to use the object helper", () => { + const output = processString("object is {{ object obj }}", { + obj: { a: 1 }, + }) + expect(output).toBe("object is {\"a\":1}") + }) +}) \ No newline at end of file