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