Adding the examples and helper add functionality for JS as well as hiding button to convert outside of development environment.

This commit is contained in:
mike12345567 2022-07-29 14:12:04 +01:00
parent 15b275c0f9
commit 67dd1fd9c3
6 changed files with 129 additions and 26 deletions

View File

@ -24,6 +24,7 @@
import { addHBSBinding, addJSBinding } from "./utils" import { addHBSBinding, addJSBinding } from "./utils"
import CodeMirrorEditor from "components/common/CodeMirrorEditor.svelte" import CodeMirrorEditor from "components/common/CodeMirrorEditor.svelte"
import { convertToJS } from "@budibase/string-templates" import { convertToJS } from "@budibase/string-templates"
import { admin } from "stores/portal"
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
@ -62,16 +63,24 @@
const updateValue = val => { const updateValue = val => {
valid = isValid(readableToRuntimeBinding(bindings, val)) valid = isValid(readableToRuntimeBinding(bindings, val))
console.log(readableToRuntimeBinding(bindings, val))
if (valid) { if (valid) {
dispatch("change", val) dispatch("change", val)
} }
} }
// Adds a HBS helper to the expression // Adds a JS/HBS helper to the expression
const addHelper = helper => { const addHelper = (helper, js) => {
hbsValue = addHBSBinding(hbsValue, getCaretPosition(), helper.text) let value
updateValue(hbsValue) const pos = getCaretPosition()
if (js) {
const decoded = decodeJSBinding(jsValue)
value = jsValue = encodeJSBinding(
addJSBinding(decoded, pos, helper.text, { helper: true })
)
} else {
value = hbsValue = addHBSBinding(hbsValue, pos, helper.text)
}
updateValue(value)
} }
// Adds a data binding to the expression // Adds a data binding to the expression
@ -108,7 +117,6 @@
const convert = () => { const convert = () => {
const runtime = readableToRuntimeBinding(bindings, hbsValue) const runtime = readableToRuntimeBinding(bindings, hbsValue)
console.log(runtime)
const runtimeJs = encodeJSBinding(convertToJS(runtime)) const runtimeJs = encodeJSBinding(convertToJS(runtime))
jsValue = runtimeToReadableBinding(bindings, runtimeJs) jsValue = runtimeToReadableBinding(bindings, runtimeJs)
hbsValue = null hbsValue = null
@ -116,6 +124,17 @@
addBinding("", { forceJS: true }) addBinding("", { forceJS: true })
} }
const getHelperExample = (helper, js) => {
let example = helper.example || ""
if (js) {
example = convertToJS(example).split("\n")[0].split("= ")[1]
if (example === "null;") {
example = ""
}
}
return example || ""
}
onMount(() => { onMount(() => {
valid = isValid(readableToRuntimeBinding(bindings, value)) valid = isValid(readableToRuntimeBinding(bindings, value))
}) })
@ -151,18 +170,21 @@
</section> </section>
{/if} {/if}
{/each} {/each}
{#if filteredHelpers?.length && !usingJS} {#if filteredHelpers?.length}
<section> <section>
<div class="heading">Helpers</div> <div class="heading">Helpers</div>
<ul> <ul>
{#each filteredHelpers as helper} {#each filteredHelpers as helper}
<li on:click={() => addHelper(helper)}> <li on:click={() => addHelper(helper, usingJS)}>
<div class="helper"> <div class="helper">
<div class="helper__name">{helper.displayText}</div> <div class="helper__name">{helper.displayText}</div>
<div class="helper__description"> <div class="helper__description">
{@html helper.description} {@html helper.description}
</div> </div>
<pre class="helper__example">{helper.example || ""}</pre> <pre class="helper__example">{getHelperExample(
helper,
usingJS
)}</pre>
</div> </div>
</li> </li>
{/each} {/each}
@ -188,9 +210,11 @@
for more details. for more details.
</p> </p>
{/if} {/if}
<div class="convert"> {#if $admin.isDev}
<Button secondary on:click={convert}>Convert to JS</Button> <div class="convert">
</div> <Button secondary on:click={convert}>Convert to JS</Button>
</div>
{/if}
</div> </div>
</Tab> </Tab>
{#if allowJS} {#if allowJS}

View File

@ -18,10 +18,14 @@ export function addHBSBinding(value, caretPos, binding) {
return value return value
} }
export function addJSBinding(value, caretPos, binding) { export function addJSBinding(value, caretPos, binding, { helper } = {}) {
binding = typeof binding === "string" ? binding : binding.path binding = typeof binding === "string" ? binding : binding.path
value = value == null ? "" : value value = value == null ? "" : value
binding = `$("${binding}")` if (!helper) {
binding = `$("${binding}")`
} else {
binding = `helper.${binding}()`
}
if (caretPos.start) { if (caretPos.start) {
value = value =
value.substring(0, caretPos.start) + value.substring(0, caretPos.start) +

View File

@ -74,7 +74,7 @@
"b" "b"
], ],
"numArgs": 2, "numArgs": 2,
"example": "{{ product 10 5 }} -> 50", "example": "{{ multiply 10 5 }} -> 50",
"description": "<p>Return the product of <code>a</code> times <code>b</code>.</p>\n" "description": "<p>Return the product of <code>a</code> times <code>b</code>.</p>\n"
}, },
"plus": { "plus": {

View File

@ -108,6 +108,10 @@ function getCommentInfo(file, func) {
if (examples.length > 0) { if (examples.length > 0) {
docs.example = examples.join(" ") docs.example = examples.join(" ")
} }
// hacky example fix
if (docs.example && docs.example.includes("product")) {
docs.example = docs.example.replace("product", "multiply")
}
docs.description = blocks[0].trim() docs.description = blocks[0].trim()
return docs return docs
} }
@ -166,7 +170,7 @@ function run() {
// convert all markdown to HTML // convert all markdown to HTML
for (let collection of Object.values(outputJSON)) { for (let collection of Object.values(outputJSON)) {
for (let helper of Object.values(collection)) { for (let helper of Object.values(collection)) {
helper.description = marked(helper.description) helper.description = marked.parse(helper.description)
} }
} }
fs.writeFileSync(FILENAME, JSON.stringify(outputJSON, null, 2)) fs.writeFileSync(FILENAME, JSON.stringify(outputJSON, null, 2))

View File

@ -22,7 +22,29 @@ function getLayers(fullBlock) {
} }
function getVariable(variableName) { function getVariable(variableName) {
return isNaN(parseFloat(variableName)) ? `$("${variableName}")` : 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 buildList(parts, value) {
@ -41,17 +63,34 @@ function buildList(parts, value) {
function splitBySpace(layer) { function splitBySpace(layer) {
const parts = [] const parts = []
let started = null, let started = null,
endChar = null,
last = 0 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++) { for (let index = 0; index < layer.length; index++) {
const char = layer[index] const char = layer[index]
if (char === "[" && started == null) { if (continuationChars.indexOf(char) !== -1 && started == null) {
started = index started = index
} else if (char === "]" && started != null && layer[index + 1] !== ".") { endChar = char === "[" ? "]" : char
parts.push(layer.substring(started, index + 1).trim()) } else if (
char === endChar &&
started != null &&
layer[index + 1] !== "."
) {
add(layer.substring(started, index + 1))
started = null started = null
last = index endChar = null
} else if (started == null && char === " " && last !== index - 1) { last = index + 1
parts.push(layer.substring(last, index).trim()) } else if (started == null && char === " ") {
add(layer.substring(last, index))
last = index last = index
} }
} }
@ -59,7 +98,7 @@ function splitBySpace(layer) {
(!layer.startsWith("[") || parts.length === 0) && (!layer.startsWith("[") || parts.length === 0) &&
last !== layer.length - 1 last !== layer.length - 1
) { ) {
parts.push(layer.substring(last, layer.length).trim()) add(layer.substring(last, layer.length))
} }
return parts return parts
} }

View File

@ -41,11 +41,43 @@ describe("Test that the string processing works correctly", () => {
]) ])
}) })
it("Should handle many square brackets in helpers", () => { it("should handle many square brackets in helpers", () => {
const response = convertToJS("Hello {{ avg [user].[_id] [user].[_rev] }}") const response = convertToJS("Hello {{ avg [user].[_id] [user].[_rev] }}")
checkLines(response, [ checkLines(response, [
"const var1 = helpers.avg($(\"[user].[_id]\"), $(\"[user].[_rev]\"));", "const var1 = helpers.avg($(\"[user].[_id]\"), $(\"[user].[_rev]\"));",
"return `Hello ${var1}`;" "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}`;",
]) ])
}) })