57 lines
1.7 KiB
JavaScript
57 lines
1.7 KiB
JavaScript
const { atob } = require("../utilities")
|
|
const { cloneDeep } = require("lodash/fp")
|
|
const { LITERAL_MARKER } = require("../helpers/constants")
|
|
|
|
// The method of executing JS scripts depends on the bundle being built.
|
|
// This setter is used in the entrypoint (either index.cjs or index.mjs).
|
|
let runJS
|
|
module.exports.setJSRunner = runner => (runJS = runner)
|
|
|
|
// Helper utility to strip square brackets from a value
|
|
const removeSquareBrackets = value => {
|
|
if (!value || typeof value !== "string") {
|
|
return value
|
|
}
|
|
const regex = /\[+(.+)]+/
|
|
const matches = value.match(regex)
|
|
if (matches && matches[1]) {
|
|
return matches[1]
|
|
}
|
|
return value
|
|
}
|
|
|
|
// Our context getter function provided to JS code as $.
|
|
// Extracts a value from context.
|
|
const getContextValue = (path, context) => {
|
|
let data = context
|
|
path.split(".").forEach(key => {
|
|
if (data == null || typeof data !== "object") {
|
|
return null
|
|
}
|
|
data = data[removeSquareBrackets(key)]
|
|
})
|
|
return data
|
|
}
|
|
|
|
// Evaluates JS code against a certain context
|
|
module.exports.processJS = (handlebars, context) => {
|
|
try {
|
|
// Wrap JS in a function and immediately invoke it.
|
|
// This is required to allow the final `return` statement to be valid.
|
|
const js = `function run(){${atob(handlebars)}};run();`
|
|
|
|
// Our $ context function gets a value from context.
|
|
// We clone the context to avoid mutation in the binding affecting real
|
|
// app context.
|
|
const sandboxContext = {
|
|
$: path => getContextValue(path, cloneDeep(context)),
|
|
}
|
|
|
|
// Create a sandbox with our context and run the JS
|
|
const res = { data: runJS(js, sandboxContext) }
|
|
return `{{${LITERAL_MARKER} js_result-${JSON.stringify(res)}}}`
|
|
} catch (error) {
|
|
return "Error while executing JS"
|
|
}
|
|
}
|