diff --git a/packages/builder/src/components/common/Dropdowns/DropdownItem.svelte b/packages/builder/src/components/common/Dropdowns/DropdownItem.svelte index f6f1ec2d12..98fda9efb1 100644 --- a/packages/builder/src/components/common/Dropdowns/DropdownItem.svelte +++ b/packages/builder/src/components/common/Dropdowns/DropdownItem.svelte @@ -11,9 +11,7 @@ on:click class:big={subtitle != null} {...$$restProps}> - {#if icon} - - {/if} + {#if icon}{/if}
{title}
{#if subtitle != null} diff --git a/packages/builder/src/components/design/PropertiesPanel/BindingPanel.svelte b/packages/builder/src/components/design/PropertiesPanel/BindingPanel.svelte index 1e40e8e001..6ab2504077 100644 --- a/packages/builder/src/components/design/PropertiesPanel/BindingPanel.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/BindingPanel.svelte @@ -1,12 +1,6 @@ - + diff --git a/packages/string-templates/jest.config.js b/packages/string-templates/jest.config.js index 3c3d264fa5..c6391cdb92 100644 --- a/packages/string-templates/jest.config.js +++ b/packages/string-templates/jest.config.js @@ -191,4 +191,4 @@ module.exports = { // Whether to use watchman for file crawling // watchman: true, -}; +} diff --git a/packages/string-templates/rollup.config.js b/packages/string-templates/rollup.config.js index fa215261dc..f4cf851f92 100644 --- a/packages/string-templates/rollup.config.js +++ b/packages/string-templates/rollup.config.js @@ -11,7 +11,7 @@ export default { name: "string-templates", exports: "named", globals: { - "fs": "fs", + fs: "fs", }, }, external: ["fs"], diff --git a/packages/string-templates/src/custom/preprocessor.js b/packages/string-templates/src/custom/preprocessor.js index f562c67c6a..ffd81f1693 100644 --- a/packages/string-templates/src/custom/preprocessor.js +++ b/packages/string-templates/src/custom/preprocessor.js @@ -1,60 +1,81 @@ const { HelperFunctions } = require("../helpers") -const { swapStrings, isAlphaNumeric, FIND_HBS_REGEX, includesAny } = require("../utilities") +const { + swapStrings, + isAlphaNumeric, + FIND_HBS_REGEX, + includesAny, +} = require("../utilities") -function handleProcessor(string, match, fn) { - const output = fn(match) - const idx = string.indexOf(match) - return swapStrings(string, idx, match.length, output) +class Preprocessor { + constructor(name, fn) { + this.name = name + this.fn = fn + } + + process(fullString, statement) { + const output = this.fn(statement) + const idx = fullString.indexOf(statement) + return swapStrings(fullString, idx, statement.length, output) + } } -function swapToDotNotation(statement) { - let startBraceIdx = statement.indexOf("[") - let lastIdx = 0 - while (startBraceIdx !== -1) { - // if the character previous to the literal specifier is alpha-numeric this should happen - if (isAlphaNumeric(statement.charAt(startBraceIdx - 1))) { - statement = swapStrings(statement, startBraceIdx + lastIdx, 1, ".[") +const PROCESSORS = [ + new Preprocessor("swap-to-dot-notation", statement => { + let startBraceIdx = statement.indexOf("[") + let lastIdx = 0 + while (startBraceIdx !== -1) { + // if the character previous to the literal specifier is alpha-numeric this should happen + if (isAlphaNumeric(statement.charAt(startBraceIdx - 1))) { + statement = swapStrings(statement, startBraceIdx + lastIdx, 1, ".[") + } + lastIdx = startBraceIdx + 1 + startBraceIdx = statement.substring(lastIdx + 1).indexOf("[") } - lastIdx = startBraceIdx + 1 - startBraceIdx = statement.substring(lastIdx + 1).indexOf("[") - } - return statement -} + return statement + }), -function handleSpacesInProperties(statement) { - // exclude helpers and brackets, regex will only find double brackets - const exclusions = HelperFunctions.concat(["{{", "}}"]) - // find all the parts split by spaces - const splitBySpaces = statement.split(" ") - // remove the excluded elements - const propertyParts = splitBySpaces.filter(part => exclusions.indexOf(part) === -1) - // rebuild to get the full property - const fullProperty = propertyParts.join(" ") - // now work out the dot notation layers and split them up - const propertyLayers = fullProperty.split(".") - // find the layers which need to be wrapped and wrap them - for (let layer of propertyLayers) { - if (layer.indexOf(" ") !== -1) { - statement = swapStrings(statement, statement.indexOf(layer), layer.length, `[${layer}]`) + new Preprocessor("handle-spaces-in-properties", statement => { + // exclude helpers and brackets, regex will only find double brackets + const exclusions = HelperFunctions.concat(["{{", "}}"]) + // find all the parts split by spaces + const splitBySpaces = statement.split(" ") + // remove the excluded elements + const propertyParts = splitBySpaces.filter( + part => exclusions.indexOf(part) === -1 + ) + // rebuild to get the full property + const fullProperty = propertyParts.join(" ") + // now work out the dot notation layers and split them up + const propertyLayers = fullProperty.split(".") + // find the layers which need to be wrapped and wrap them + for (let layer of propertyLayers) { + if (layer.indexOf(" ") !== -1) { + statement = swapStrings( + statement, + statement.indexOf(layer), + layer.length, + `[${layer}]` + ) + } } - } - // remove the edge case of double brackets being entered (in-case user already has specified) - return statement.replace(/\[\[/g, "[").replace(/]]/g, "]") -} + // remove the edge case of double brackets being entered (in-case user already has specified) + return statement.replace(/\[\[/g, "[").replace(/]]/g, "]") + }), -function finalise(statement) { - let insideStatement = statement.slice(2, statement.length - 2) - if (insideStatement.charAt(0) === " ") { - insideStatement = insideStatement.slice(1) - } - if (insideStatement.charAt(insideStatement.length - 1) === " ") { - insideStatement = insideStatement.slice(0, insideStatement.length - 1) - } - if (includesAny(insideStatement, HelperFunctions)) { - insideStatement = `(${insideStatement})` - } - return `{{ all ${insideStatement} }}` -} + new Preprocessor("finalise", statement => { + let insideStatement = statement.slice(2, statement.length - 2) + if (insideStatement.charAt(0) === " ") { + insideStatement = insideStatement.slice(1) + } + if (insideStatement.charAt(insideStatement.length - 1) === " ") { + insideStatement = insideStatement.slice(0, insideStatement.length - 1) + } + if (includesAny(insideStatement, HelperFunctions)) { + insideStatement = `(${insideStatement})` + } + return `{{ all ${insideStatement} }}` + }) +] /** * When running handlebars statements to execute on the context of the automation it possible user's may input handlebars @@ -67,18 +88,17 @@ function finalise(statement) { * @param {string} string The string which *may* contain handlebars statements, it is OK if it does not contain any. * @returns {string} The string that was input with processed up handlebars statements as required. */ -module.exports.preprocess = (string) => { - let preprocessors = [swapToDotNotation, handleSpacesInProperties, finalise] - for (let processor of preprocessors) { - // re-run search each time incase previous cleaner update/removed a match +module.exports.preprocess = string => { + for (let processor of PROCESSORS) { + // re-run search each time incase previous processor updated/removed a match let regex = new RegExp(FIND_HBS_REGEX) let matches = string.match(regex) if (matches == null) { continue } for (let match of matches) { - string = handleProcessor(string, match, processor) + string = processor.process(string, match) } } return string -} \ No newline at end of file +} diff --git a/packages/string-templates/src/helpers/index.js b/packages/string-templates/src/helpers/index.js index 498ac40dce..768fe297af 100644 --- a/packages/string-templates/src/helpers/index.js +++ b/packages/string-templates/src/helpers/index.js @@ -12,7 +12,7 @@ const HelperFunctionBuiltin = [ "#each", "#with", "lookup", - "log" + "log", ] const HelperFunctionNames = { @@ -27,17 +27,19 @@ const HELPERS = [ }), // this help is applied to all statements new Helper(HelperFunctionNames.ALL, value => { - let text = new SafeString(unescape(value).replace(/&/g, '&')) + let text = new SafeString(unescape(value).replace(/&/g, "&")) if (text == null || typeof text !== "string") { return text } return text.replace(/[<>]/g, tag => { return HTML_SWAPS[tag] || tag }) - }) + }), ] -module.exports.HelperFunctions = Object.values(HelperFunctionNames).concat(HelperFunctionBuiltin) +module.exports.HelperFunctions = Object.values(HelperFunctionNames).concat( + HelperFunctionBuiltin +) module.exports.registerAll = handlebars => { for (let helper of HELPERS) { diff --git a/packages/string-templates/src/utilities.js b/packages/string-templates/src/utilities.js index c7179aac75..bb35e94567 100644 --- a/packages/string-templates/src/utilities.js +++ b/packages/string-templates/src/utilities.js @@ -1,8 +1,8 @@ const ALPHA_NUMERIC_REGEX = /^[A-Za-z0-9]+$/g -module.exports.FIND_HBS_REGEX = /{{[^}}]*}}/g +module.exports.FIND_HBS_REGEX = /{{([^{}])+}}/g -module.exports.isAlphaNumeric = (char) => { +module.exports.isAlphaNumeric = char => { return char.match(ALPHA_NUMERIC_REGEX) } diff --git a/packages/string-templates/test/basic.spec.js b/packages/string-templates/test/basic.spec.js index 172c94ca21..6babce8f8c 100644 --- a/packages/string-templates/test/basic.spec.js +++ b/packages/string-templates/test/basic.spec.js @@ -11,6 +11,13 @@ describe("Test that the string processing works correctly", () => { expect(output).toBe("templating is easy") }) + it("should process a literal template", async () => { + const output = await processString("derp is {{{ adjective }}}", { + adjective: "derp" + }) + expect(output).toBe("derp is derp") + }) + it("should fail gracefully when wrong type passed in", async () => { let error = null try { diff --git a/packages/string-templates/test/escapes.spec.js b/packages/string-templates/test/escapes.spec.js index eb94b1ce2e..39df8719d6 100644 --- a/packages/string-templates/test/escapes.spec.js +++ b/packages/string-templates/test/escapes.spec.js @@ -32,4 +32,20 @@ describe("Handling context properties with spaces in their name", () => { }) expect(output).toBe("testcase 1") }) +}) + +describe("attempt some complex problems", () => { + it("should be able to handle a very complex handlebars statement", async () => { + const context = { + "New Repeater": { + "Get Actors": { + "first_name": "Bob", + "last_name": "Bobert" + }, + }, + } + const hbs = "{{ New Repeater.Get Actors.first_name }} {{ New Repeater.Get Actors.last_name }}" + const output = await processString(hbs, context) + expect(output).toBe("Bob Bobert") + }) }) \ No newline at end of file diff --git a/packages/string-templates/test/renderApp.spec.js b/packages/string-templates/test/renderApp.spec.js new file mode 100644 index 0000000000..1ae08ee113 --- /dev/null +++ b/packages/string-templates/test/renderApp.spec.js @@ -0,0 +1,33 @@ +const { processString } = require("../src/index") + +describe("specific test case for whether or not full app template can still be rendered", () => { + it("should be able to render the app template", async () => { + const template = + ` + + + {{{head}}} + + + {{{body}}} + ` + const context = { + appId: "App1", + head: "App", + body: "

App things

" + } + const output = await processString(template, context) + expect(output).toBe(` + + + App + + +

App things

+ `) + }) +}) \ No newline at end of file