Moving everything over to use the string template library, now just need to escape spaces properly and handle HTML escaping.

This commit is contained in:
mike12345567 2021-01-19 18:44:29 +00:00
parent e8ef03bb1c
commit 4c597ed91a
13 changed files with 70 additions and 43 deletions

View File

@ -76,7 +76,6 @@
"deepmerge": "^4.2.2", "deepmerge": "^4.2.2",
"fast-sort": "^2.2.0", "fast-sort": "^2.2.0",
"lodash": "^4.17.13", "lodash": "^4.17.13",
"mustache": "^4.0.1",
"posthog-js": "1.4.5", "posthog-js": "1.4.5",
"remixicon": "^2.5.0", "remixicon": "^2.5.0",
"shortid": "^2.2.15", "shortid": "^2.2.15",

View File

@ -1,8 +1,8 @@
export const CAPTURE_VAR_INSIDE_MUSTACHE = /{{([^}]+)}}/g export const CAPTURE_VAR_INSIDE_TEMPLATE = /{{([^}]+)}}/g
export function readableToRuntimeBinding(bindableProperties, textWithBindings) { export function readableToRuntimeBinding(bindableProperties, textWithBindings) {
// Find all instances of mustasche // Find all instances of template strings
const boundValues = textWithBindings.match(CAPTURE_VAR_INSIDE_MUSTACHE) const boundValues = textWithBindings.match(CAPTURE_VAR_INSIDE_TEMPLATE)
let result = textWithBindings let result = textWithBindings
// Replace readableBindings with runtimeBindings // Replace readableBindings with runtimeBindings
@ -22,7 +22,7 @@ export function runtimeToReadableBinding(bindableProperties, textWithBindings) {
let temp = textWithBindings let temp = textWithBindings
const boundValues = const boundValues =
(typeof textWithBindings === "string" && (typeof textWithBindings === "string" &&
textWithBindings.match(CAPTURE_VAR_INSIDE_MUSTACHE)) || textWithBindings.match(CAPTURE_VAR_INSIDE_TEMPLATE)) ||
[] []
// Replace runtimeBindings with readableBindings: // Replace runtimeBindings with readableBindings:

View File

@ -1,6 +1,6 @@
export function uuid() { export function uuid() {
// always want to make this start with a letter, as this makes it // 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 => { return "cxxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace(/[xy]/g, c => {
const r = (Math.random() * 16) | 0, const r = (Math.random() * 16) | 0,
v = c == "x" ? r : (r & 0x3) | 0x8 v = c == "x" ? r : (r & 0x3) | 0x8

View File

@ -1,5 +1,5 @@
<script> <script>
import mustache from "mustache" import { processString } from "@budibase/string-templates"
import { get } from "lodash/fp" import { get } from "lodash/fp"
import { backendUiStore } from "builderStore" import { backendUiStore } from "builderStore"
@ -54,8 +54,8 @@
} }
}) })
// Fill in bindings with handlebars // Fill in bindings with templating library
return mustache.render(formattedTagline, { inputs }) return processString(formattedTagline, { inputs })
} }
</script> </script>

View File

@ -5324,11 +5324,6 @@ ms@2.1.2, ms@^2.1.1:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 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: nan@^2.12.1:
version "2.14.2" version "2.14.2"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"

View File

@ -11,7 +11,6 @@
"dependencies": { "dependencies": {
"@budibase/string-templates": "^0.5.3", "@budibase/string-templates": "^0.5.3",
"deep-equal": "^2.0.1", "deep-equal": "^2.0.1",
"mustache": "^4.0.1",
"regexparam": "^1.3.0", "regexparam": "^1.3.0",
"svelte-spa-router": "^3.0.5" "svelte-spa-router": "^3.0.5"
}, },

View File

@ -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 // Regex to test inputs with to see if they are likely candidates for template strings
// ...just ignoring < and > to prevent tags from user input const looksLikeTemplate = /{{.*}}/
// original version here https://github.com/janl/mustache.js/blob/4b7908f5c9fec469a11cfaed2f2bed23c84e1c5c/mustache.js#L78
const entityMap = {
"<": "&lt;",
">": "&gt;",
}
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 = /{{.*}}/
/** /**
* Enriches a given input with a row from the database. * Enriches a given input with a row from the database.
@ -27,11 +11,11 @@ export const enrichDataBinding = (input, context) => {
if (!input || typeof input !== "string") { if (!input || typeof input !== "string") {
return input return input
} }
// Do a fast regex check if this looks like a mustache string // Do a fast regex check if this looks like a template string
if (!looksLikeMustache.test(input)) { if (!looksLikeTemplate.test(input)) {
return input return input
} }
return mustache.render(input, context) return processString(input, context)
} }
/** /**

View File

@ -1362,11 +1362,6 @@ minimatch@^3.0.4:
dependencies: dependencies:
brace-expansion "^1.1.7" 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: nwsapi@^2.2.0:
version "2.2.0" version "2.2.0"
resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7"

View File

@ -5,7 +5,6 @@
"main": "dist/bundle.js", "main": "dist/bundle.js",
"module": "dist/bundle.js", "module": "dist/bundle.js",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"private": true,
"types": "dist/index.d.ts", "types": "dist/index.d.ts",
"scripts": { "scripts": {
"build": "rollup -c", "build": "rollup -c",

View File

@ -1,10 +1,26 @@
const Helper = require("./Helper") const Helper = require("./Helper")
const { SafeString } = require("handlebars") const { SafeString } = require("handlebars")
const HTML_SWAPS = {
"<": "&lt;",
">": "&gt;",
}
const HELPERS = [ const HELPERS = [
// external helpers
new Helper("object", value => { new Helper("object", value => {
return new SafeString(JSON.stringify(value)) return new SafeString(JSON.stringify(value))
}), }),
// this help is applied to all statements
new Helper("all", value => {
let text = unescape(value).replace(/&amp;/g, '&');
if (text == null || typeof text !== "string") {
return text
}
return text.replace(/[<>]/g, tag => {
return HTML_SWAPS[tag] || tag
})
})
] ]
module.exports.registerAll = handlebars => { module.exports.registerAll = handlebars => {

View File

@ -2,6 +2,7 @@ const handlebars = require("handlebars")
const { registerAll } = require("./helpers/index") const { registerAll } = require("./helpers/index")
const HBS_CLEANING_REGEX = /{{[^}}]*}}/g const HBS_CLEANING_REGEX = /{{[^}}]*}}/g
const FIND_HBS_REGEX = /{{.*}}/
const hbsInstance = handlebars.create() const hbsInstance = handlebars.create()
registerAll(hbsInstance) registerAll(hbsInstance)
@ -22,6 +23,12 @@ function attemptToCorrectError(string) {
* @returns {string} The string that was input with cleaned up handlebars statements as required. * @returns {string} The string that was input with cleaned up handlebars statements as required.
*/ */
function cleanHandlebars(string) { 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 = { let charToReplace = {
"[": ".", "[": ".",
"]": "", "]": "",

View File

@ -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")
})
})

View File

@ -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}")
})
})