forked from tsai/budibase
committed by
GitHub
13 changed files with 388 additions and 14 deletions
@ -0,0 +1,129 @@ |
|||||
|
const { getHelperList } = require("../helpers") |
||||
|
|
||||
|
function getLayers(fullBlock) { |
||||
|
let layers = [] |
||||
|
while (fullBlock.length) { |
||||
|
const start = fullBlock.lastIndexOf("("), |
||||
|
end = fullBlock.indexOf(")") |
||||
|
let layer |
||||
|
if (start === -1 || end === -1) { |
||||
|
layer = fullBlock.trim() |
||||
|
fullBlock = "" |
||||
|
} else { |
||||
|
const untrimmed = fullBlock.substring(start, end + 1) |
||||
|
layer = untrimmed.substring(1, untrimmed.length - 1).trim() |
||||
|
fullBlock = |
||||
|
fullBlock.slice(0, start) + |
||||
|
fullBlock.slice(start + untrimmed.length + 1, fullBlock.length) |
||||
|
} |
||||
|
layers.push(layer) |
||||
|
} |
||||
|
return layers |
||||
|
} |
||||
|
|
||||
|
function getVariable(variableName) { |
||||
|
if (!variableName || typeof variableName !== "string") { |
||||
|
return variableName |
||||
|
} |
||||
|
// it is an array
|
||||
|
const arrayOrObject = [",", "{", ":"] |
||||
|
let contains = false |
||||
|
arrayOrObject.forEach(char => { |
||||
|
if (variableName.includes(char)) { |
||||
|
contains = true |
||||
|
} |
||||
|
}) |
||||
|
if (variableName.startsWith("[") && contains) { |
||||
|
return variableName |
||||
|
} |
||||
|
// it is just a number
|
||||
|
if (!isNaN(parseFloat(variableName))) { |
||||
|
return variableName |
||||
|
} |
||||
|
if (variableName.startsWith("'") || variableName.startsWith('"')) { |
||||
|
return variableName |
||||
|
} |
||||
|
// extract variable
|
||||
|
return `$("${variableName}")` |
||||
|
} |
||||
|
|
||||
|
function buildList(parts, value) { |
||||
|
function build() { |
||||
|
return parts |
||||
|
.map(part => (part.startsWith("helper") ? part : getVariable(part))) |
||||
|
.join(", ") |
||||
|
} |
||||
|
if (!value) { |
||||
|
return parts.length > 1 ? `${build()}` : build() |
||||
|
} else { |
||||
|
return parts.length === 0 ? value : `${value}, ${build()}` |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function splitBySpace(layer) { |
||||
|
const parts = [] |
||||
|
let started = null, |
||||
|
endChar = null, |
||||
|
last = 0 |
||||
|
function add(str) { |
||||
|
const startsWith = ["]"] |
||||
|
while (startsWith.indexOf(str.substring(0, 1)) !== -1) { |
||||
|
str = str.substring(1, str.length) |
||||
|
} |
||||
|
if (str.length > 0) { |
||||
|
parts.push(str.trim()) |
||||
|
} |
||||
|
} |
||||
|
const continuationChars = ["[", "'", '"'] |
||||
|
for (let index = 0; index < layer.length; index++) { |
||||
|
const char = layer[index] |
||||
|
if (continuationChars.indexOf(char) !== -1 && started == null) { |
||||
|
started = index |
||||
|
endChar = char === "[" ? "]" : char |
||||
|
} else if ( |
||||
|
char === endChar && |
||||
|
started != null && |
||||
|
layer[index + 1] !== "." |
||||
|
) { |
||||
|
add(layer.substring(started, index + 1)) |
||||
|
started = null |
||||
|
endChar = null |
||||
|
last = index + 1 |
||||
|
} else if (started == null && char === " ") { |
||||
|
add(layer.substring(last, index)) |
||||
|
last = index |
||||
|
} |
||||
|
} |
||||
|
if ( |
||||
|
(!layer.startsWith("[") || parts.length === 0) && |
||||
|
last !== layer.length - 1 |
||||
|
) { |
||||
|
add(layer.substring(last, layer.length)) |
||||
|
} |
||||
|
return parts |
||||
|
} |
||||
|
|
||||
|
module.exports.convertHBSBlock = (block, blockNumber) => { |
||||
|
const braceLength = block[2] === "{" ? 3 : 2 |
||||
|
block = block.substring(braceLength, block.length - braceLength).trim() |
||||
|
const layers = getLayers(block) |
||||
|
|
||||
|
let value = null |
||||
|
const list = getHelperList() |
||||
|
for (let layer of layers) { |
||||
|
const parts = splitBySpace(layer) |
||||
|
if (value || parts.length > 1) { |
||||
|
// first of layer should always be the helper
|
||||
|
const helper = parts.splice(0, 1) |
||||
|
if (list[helper]) { |
||||
|
value = `helpers.${helper}(${buildList(parts, value)})` |
||||
|
} |
||||
|
} |
||||
|
// no helpers
|
||||
|
else { |
||||
|
value = getVariable(parts[0]) |
||||
|
} |
||||
|
} |
||||
|
// split by space will remove square brackets
|
||||
|
return { variable: `var${blockNumber}`, value } |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
const externalHandlebars = require("./external") |
||||
|
const helperList = require("@budibase/handlebars-helpers") |
||||
|
|
||||
|
module.exports.getHelperList = () => { |
||||
|
let constructed = [] |
||||
|
for (let collection of externalHandlebars.externalCollections) { |
||||
|
constructed.push(helperList[collection]()) |
||||
|
} |
||||
|
const fullMap = {} |
||||
|
for (let collection of constructed) { |
||||
|
for (let [key, func] of Object.entries(collection)) { |
||||
|
fullMap[key] = func |
||||
|
} |
||||
|
} |
||||
|
for (let key of Object.keys(externalHandlebars.addedHelpers)) { |
||||
|
fullMap[key] = externalHandlebars.addedHelpers[key] |
||||
|
} |
||||
|
return fullMap |
||||
|
} |
||||
@ -0,0 +1,132 @@ |
|||||
|
const { |
||||
|
convertToJS |
||||
|
} = require("../src/index.cjs") |
||||
|
|
||||
|
function checkLines(response, lines) { |
||||
|
const toCheck = response.split("\n") |
||||
|
let count = 0 |
||||
|
for (let line of lines) { |
||||
|
expect(toCheck[count++]).toBe(line) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
describe("Test that the string processing works correctly", () => { |
||||
|
it("should convert string without HBS", () => { |
||||
|
const response = convertToJS("Hello my name is Michael") |
||||
|
expect(response).toBe("return `Hello my name is Michael`;") |
||||
|
}) |
||||
|
|
||||
|
it("basic example with square brackets", () => { |
||||
|
const response = convertToJS("{{ [query] }}") |
||||
|
checkLines(response, [ |
||||
|
"const var1 = $(\"[query]\");", |
||||
|
"return `${var1}`;", |
||||
|
]) |
||||
|
}) |
||||
|
|
||||
|
it("handle properties", () => { |
||||
|
const response = convertToJS("{{ [query].id }}") |
||||
|
checkLines(response, [ |
||||
|
"const var1 = $(\"[query].id\");", |
||||
|
"return `${var1}`;", |
||||
|
]) |
||||
|
}) |
||||
|
|
||||
|
it("should convert some basic HBS strings", () => { |
||||
|
const response = convertToJS("Hello {{ name }}, welcome to {{ company }}!") |
||||
|
checkLines(response, [ |
||||
|
"const var1 = $(\"name\");", |
||||
|
"const var2 = $(\"company\");", |
||||
|
"return `Hello ${var1}, welcome to ${var2}`;", |
||||
|
]) |
||||
|
}) |
||||
|
|
||||
|
it("should handle many square brackets in helpers", () => { |
||||
|
const response = convertToJS("Hello {{ avg [user].[_id] [user].[_rev] }}") |
||||
|
checkLines(response, [ |
||||
|
"const var1 = helpers.avg($(\"[user].[_id]\"), $(\"[user].[_rev]\"));", |
||||
|
"return `Hello ${var1}`;", |
||||
|
]) |
||||
|
}) |
||||
|
|
||||
|
it("should handle one of the examples (after)", () => { |
||||
|
const response = convertToJS("{{ after [1, 2, 3] 1}}") |
||||
|
checkLines(response, [ |
||||
|
"const var1 = helpers.after([1, 2, 3], 1);", |
||||
|
"return `${var1}`;", |
||||
|
]) |
||||
|
}) |
||||
|
|
||||
|
it("should handle one of the examples (equalsLength)", () => { |
||||
|
const response = convertToJS("{{equalsLength '[1,2,3]' 3}}") |
||||
|
checkLines(response, [ |
||||
|
"const var1 = helpers.equalsLength('[1,2,3]', 3);", |
||||
|
"return `${var1}`;" |
||||
|
]) |
||||
|
}) |
||||
|
|
||||
|
it("should handle one of the examples (pluck)", () => { |
||||
|
const response = convertToJS("{{pluck [{ 'name': 'Bob' }] 'name' }}") |
||||
|
checkLines(response, [ |
||||
|
"const var1 = helpers.pluck([{ 'name': 'Bob' }], 'name');", |
||||
|
"return `${var1}`;", |
||||
|
]) |
||||
|
}) |
||||
|
|
||||
|
it("should handle sorting an array", () => { |
||||
|
const response = convertToJS("{{ sort ['b', 'a', 'c'] }}") |
||||
|
checkLines(response, [ |
||||
|
"const var1 = helpers.sort(['b', 'a', 'c']);", |
||||
|
"return `${var1}`;", |
||||
|
]) |
||||
|
}) |
||||
|
|
||||
|
it("should handle a helper block", () => { |
||||
|
const response = convertToJS("This is the average: {{ avg array }}") |
||||
|
checkLines(response, [ |
||||
|
"const var1 = helpers.avg($(\"array\"));", |
||||
|
"return `This is the average: ${var1}`;", |
||||
|
]) |
||||
|
}) |
||||
|
|
||||
|
it("should handle multi-variable helper", () => { |
||||
|
const response = convertToJS("This is the average: {{ join ( avg val1 val2 val3 ) }}") |
||||
|
checkLines(response, [ |
||||
|
"const var1 = helpers.join(helpers.avg($(\"val1\"), $(\"val2\"), $(\"val3\")));", |
||||
|
"return `This is the average: ${var1}`;", |
||||
|
]) |
||||
|
}) |
||||
|
|
||||
|
it("should handle a complex statement", () => { |
||||
|
const response = convertToJS("This is the average: {{ join ( avg val1 val2 val3 ) val4 }}") |
||||
|
checkLines(response, [ |
||||
|
"const var1 = helpers.join(helpers.avg($(\"val1\"), $(\"val2\"), $(\"val3\")), $(\"val4\"));", |
||||
|
"return `This is the average: ${var1}`;", |
||||
|
]) |
||||
|
}) |
||||
|
|
||||
|
it("should handle square brackets", () => { |
||||
|
const response = convertToJS("This is: {{ [val thing] }}") |
||||
|
checkLines(response, [ |
||||
|
"const var1 = $(\"[val thing]\");", |
||||
|
"return `This is: ${var1}`;", |
||||
|
]) |
||||
|
}) |
||||
|
|
||||
|
it("should handle square brackets with properties", () => { |
||||
|
const response = convertToJS("{{ [user].[_id] }}") |
||||
|
checkLines(response, [ |
||||
|
"const var1 = $(\"[user].[_id]\");", |
||||
|
"return `${var1}`;", |
||||
|
]) |
||||
|
}) |
||||
|
|
||||
|
it("should handle multiple complex statements", () => { |
||||
|
const response = convertToJS("average: {{ avg ( abs val1 ) val2 }} add: {{ add 1 2 }}") |
||||
|
checkLines(response, [ |
||||
|
"const var1 = helpers.avg(helpers.abs($(\"val1\")), $(\"val2\"));", |
||||
|
"const var2 = helpers.add(1, 2);", |
||||
|
"return `average: ${var1} add: ${var2}`;", |
||||
|
]) |
||||
|
}) |
||||
|
}) |
||||
Loading…
Reference in new issue