@budibase/string-templates 3.2.4 → 3.2.6
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/bundle.mjs +1 -1
- package/dist/iife.mjs +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -8
- package/src/conversion/index.ts +0 -131
- package/src/errors.ts +0 -20
- package/src/helpers/Helper.ts +0 -36
- package/src/helpers/constants.ts +0 -41
- package/src/helpers/date.ts +0 -133
- package/src/helpers/external.ts +0 -57
- package/src/helpers/index.ts +0 -103
- package/src/helpers/javascript.ts +0 -168
- package/src/helpers/list.ts +0 -72
- package/src/iife.ts +0 -3
- package/src/index.ts +0 -495
- package/src/manifest.json +0 -1364
- package/src/processors/index.ts +0 -37
- package/src/processors/postprocessor.ts +0 -57
- package/src/processors/preprocessor.ts +0 -90
- package/src/types.ts +0 -10
- package/src/utilities.ts +0 -88
package/src/helpers/index.ts
DELETED
@@ -1,103 +0,0 @@
|
|
1
|
-
import Helper from "./Helper"
|
2
|
-
import Handlebars from "handlebars"
|
3
|
-
import * as externalHandlebars from "./external"
|
4
|
-
import { processJS } from "./javascript"
|
5
|
-
import {
|
6
|
-
HelperFunctionNames,
|
7
|
-
HelperFunctionBuiltin,
|
8
|
-
LITERAL_MARKER,
|
9
|
-
} from "./constants"
|
10
|
-
|
11
|
-
export { getJsHelperList } from "./list"
|
12
|
-
|
13
|
-
const HTML_SWAPS = {
|
14
|
-
"<": "<",
|
15
|
-
">": ">",
|
16
|
-
}
|
17
|
-
|
18
|
-
function isObject(value: string | any[]) {
|
19
|
-
if (value == null || typeof value !== "object") {
|
20
|
-
return false
|
21
|
-
}
|
22
|
-
return (
|
23
|
-
value.toString() === "[object Object]" ||
|
24
|
-
(value.length > 0 && typeof value[0] === "object")
|
25
|
-
)
|
26
|
-
}
|
27
|
-
|
28
|
-
const HELPERS = [
|
29
|
-
// external helpers
|
30
|
-
new Helper(HelperFunctionNames.OBJECT, (value: any) => {
|
31
|
-
return new Handlebars.SafeString(JSON.stringify(value))
|
32
|
-
}),
|
33
|
-
// javascript helper
|
34
|
-
new Helper(HelperFunctionNames.JS, processJS, false),
|
35
|
-
// this help is applied to all statements
|
36
|
-
new Helper(
|
37
|
-
HelperFunctionNames.ALL,
|
38
|
-
(value: string, inputs: { __opts: any }) => {
|
39
|
-
const { __opts } = inputs
|
40
|
-
if (isObject(value)) {
|
41
|
-
return new Handlebars.SafeString(JSON.stringify(value))
|
42
|
-
}
|
43
|
-
// null/undefined values produce bad results
|
44
|
-
if (__opts && __opts.onlyFound && value == null) {
|
45
|
-
return __opts.input
|
46
|
-
}
|
47
|
-
if (value == null || typeof value !== "string") {
|
48
|
-
return value == null ? "" : value
|
49
|
-
}
|
50
|
-
// TODO: check, this should always be false
|
51
|
-
if (value && (value as any).string) {
|
52
|
-
value = (value as any).string
|
53
|
-
}
|
54
|
-
let text: any = value
|
55
|
-
if (__opts && __opts.escapeNewlines) {
|
56
|
-
text = value.replace(/\n/g, "\\n")
|
57
|
-
}
|
58
|
-
text = new Handlebars.SafeString(text.replace(/&/g, "&"))
|
59
|
-
if (text == null || typeof text !== "string") {
|
60
|
-
return text
|
61
|
-
}
|
62
|
-
return text.replace(/[<>]/g, (tag: string) => {
|
63
|
-
return HTML_SWAPS[tag as keyof typeof HTML_SWAPS] || tag
|
64
|
-
})
|
65
|
-
}
|
66
|
-
),
|
67
|
-
// adds a note for post-processor
|
68
|
-
new Helper(HelperFunctionNames.LITERAL, (value: any) => {
|
69
|
-
if (value === undefined) {
|
70
|
-
return ""
|
71
|
-
}
|
72
|
-
const type = typeof value
|
73
|
-
const outputVal = type === "object" ? JSON.stringify(value) : value
|
74
|
-
return `{{${LITERAL_MARKER} ${type}-${outputVal}}}`
|
75
|
-
}),
|
76
|
-
]
|
77
|
-
|
78
|
-
export function HelperNames() {
|
79
|
-
return Object.values(HelperFunctionNames).concat(
|
80
|
-
HelperFunctionBuiltin,
|
81
|
-
externalHandlebars.externalHelperNames
|
82
|
-
)
|
83
|
-
}
|
84
|
-
|
85
|
-
export function registerMinimum(handlebars: typeof Handlebars) {
|
86
|
-
for (let helper of HELPERS) {
|
87
|
-
helper.register(handlebars)
|
88
|
-
}
|
89
|
-
}
|
90
|
-
|
91
|
-
export function registerAll(handlebars: typeof Handlebars) {
|
92
|
-
registerMinimum(handlebars)
|
93
|
-
// register imported helpers
|
94
|
-
externalHandlebars.registerAll(handlebars)
|
95
|
-
}
|
96
|
-
|
97
|
-
export function unregisterAll(handlebars: any) {
|
98
|
-
for (let helper of HELPERS) {
|
99
|
-
helper.unregister(handlebars)
|
100
|
-
}
|
101
|
-
// unregister all imported helpers
|
102
|
-
externalHandlebars.unregisterAll(handlebars)
|
103
|
-
}
|
@@ -1,168 +0,0 @@
|
|
1
|
-
import { atob, isBackendService, isJSAllowed } from "../utilities"
|
2
|
-
import { LITERAL_MARKER } from "../helpers/constants"
|
3
|
-
import { getJsHelperList } from "./list"
|
4
|
-
import { iifeWrapper } from "../iife"
|
5
|
-
import { JsTimeoutError, UserScriptError } from "../errors"
|
6
|
-
import { cloneDeep } from "lodash/fp"
|
7
|
-
|
8
|
-
// The method of executing JS scripts depends on the bundle being built.
|
9
|
-
// This setter is used in the entrypoint (either index.js or index.mjs).
|
10
|
-
let runJS: ((js: string, context: Record<string, any>) => any) | undefined =
|
11
|
-
undefined
|
12
|
-
export const setJSRunner = (runner: typeof runJS) => (runJS = runner)
|
13
|
-
|
14
|
-
export const removeJSRunner = () => {
|
15
|
-
runJS = undefined
|
16
|
-
}
|
17
|
-
|
18
|
-
let onErrorLog: (message: Error) => void
|
19
|
-
export const setOnErrorLog = (delegate: typeof onErrorLog) =>
|
20
|
-
(onErrorLog = delegate)
|
21
|
-
|
22
|
-
// Helper utility to strip square brackets from a value
|
23
|
-
const removeSquareBrackets = (value: string) => {
|
24
|
-
if (!value || typeof value !== "string") {
|
25
|
-
return value
|
26
|
-
}
|
27
|
-
const regex = /\[+(.+)]+/
|
28
|
-
const matches = value.match(regex)
|
29
|
-
if (matches && matches[1]) {
|
30
|
-
return matches[1]
|
31
|
-
}
|
32
|
-
return value
|
33
|
-
}
|
34
|
-
|
35
|
-
const isReservedKey = (key: string) =>
|
36
|
-
key === "snippets" ||
|
37
|
-
key === "helpers" ||
|
38
|
-
key.startsWith("snippets.") ||
|
39
|
-
key.startsWith("helpers.")
|
40
|
-
|
41
|
-
// Our context getter function provided to JS code as $.
|
42
|
-
// Extracts a value from context.
|
43
|
-
const getContextValue = (path: string, context: any) => {
|
44
|
-
// We populate `snippets` ourselves, don't allow access to it.
|
45
|
-
if (isReservedKey(path)) {
|
46
|
-
return undefined
|
47
|
-
}
|
48
|
-
const literalStringRegex = /^(["'`]).*\1$/
|
49
|
-
let data = context
|
50
|
-
// check if it's a literal string - just return path if its quoted
|
51
|
-
if (literalStringRegex.test(path)) {
|
52
|
-
return path.substring(1, path.length - 1)
|
53
|
-
}
|
54
|
-
path.split(".").forEach(key => {
|
55
|
-
if (data == null || typeof data !== "object") {
|
56
|
-
return null
|
57
|
-
}
|
58
|
-
data = data[removeSquareBrackets(key)]
|
59
|
-
})
|
60
|
-
|
61
|
-
return data
|
62
|
-
}
|
63
|
-
|
64
|
-
// Evaluates JS code against a certain context
|
65
|
-
export function processJS(handlebars: string, context: any) {
|
66
|
-
if (!isJSAllowed() || !runJS) {
|
67
|
-
throw new Error("JS disabled in environment.")
|
68
|
-
}
|
69
|
-
try {
|
70
|
-
// Wrap JS in a function and immediately invoke it.
|
71
|
-
// This is required to allow the final `return` statement to be valid.
|
72
|
-
const js = iifeWrapper(atob(handlebars))
|
73
|
-
|
74
|
-
// Transform snippets into an object for faster access, and cache previously
|
75
|
-
// evaluated snippets
|
76
|
-
let snippetMap: any = {}
|
77
|
-
let snippetCache: any = {}
|
78
|
-
for (let snippet of context.snippets || []) {
|
79
|
-
snippetMap[snippet.name] = snippet.code
|
80
|
-
}
|
81
|
-
|
82
|
-
let clonedContext: Record<string, any>
|
83
|
-
if (isBackendService()) {
|
84
|
-
// On the backned, values are copied across the isolated-vm boundary and
|
85
|
-
// so we don't need to do any cloning here. This does create a fundamental
|
86
|
-
// difference in how JS executes on the frontend vs the backend, e.g.
|
87
|
-
// consider this snippet:
|
88
|
-
//
|
89
|
-
// $("array").push(2)
|
90
|
-
// return $("array")[1]
|
91
|
-
//
|
92
|
-
// With the context of `{ array: [1] }`, the backend will return
|
93
|
-
// `undefined` whereas the frontend will return `2`. We should fix this.
|
94
|
-
clonedContext = context
|
95
|
-
} else {
|
96
|
-
clonedContext = cloneDeep(context)
|
97
|
-
}
|
98
|
-
|
99
|
-
const sandboxContext = {
|
100
|
-
$: (path: string) => getContextValue(path, clonedContext),
|
101
|
-
helpers: getJsHelperList(),
|
102
|
-
|
103
|
-
// Proxy to evaluate snippets when running in the browser
|
104
|
-
snippets: new Proxy(
|
105
|
-
{},
|
106
|
-
{
|
107
|
-
get: function (_, name) {
|
108
|
-
if (!(name in snippetCache)) {
|
109
|
-
snippetCache[name] = eval(iifeWrapper(snippetMap[name]))
|
110
|
-
}
|
111
|
-
return snippetCache[name]
|
112
|
-
},
|
113
|
-
}
|
114
|
-
),
|
115
|
-
}
|
116
|
-
|
117
|
-
// Create a sandbox with our context and run the JS
|
118
|
-
const res = { data: runJS(js, sandboxContext) }
|
119
|
-
return `{{${LITERAL_MARKER} js_result-${JSON.stringify(res)}}}`
|
120
|
-
} catch (error: any) {
|
121
|
-
onErrorLog && onErrorLog(error)
|
122
|
-
|
123
|
-
const { noThrow = true } = context.__opts || {}
|
124
|
-
|
125
|
-
// The error handling below is quite messy, because it has fallen to
|
126
|
-
// string-templates to handle a variety of types of error specific to usages
|
127
|
-
// above it in the stack. It would be nice some day to refactor this to
|
128
|
-
// allow each user of processStringSync to handle errors in the way they see
|
129
|
-
// fit.
|
130
|
-
|
131
|
-
// This is to catch the error vm.runInNewContext() throws when the timeout
|
132
|
-
// is exceeded.
|
133
|
-
if (error.code === "ERR_SCRIPT_EXECUTION_TIMEOUT") {
|
134
|
-
return "Timed out while executing JS"
|
135
|
-
}
|
136
|
-
|
137
|
-
// This is to catch the JsRequestTimeoutError we throw when we detect a
|
138
|
-
// timeout across an entire request in the backend. We use a magic string
|
139
|
-
// because we can't import from the backend into string-templates.
|
140
|
-
if (error.code === "JS_REQUEST_TIMEOUT_ERROR") {
|
141
|
-
return error.message
|
142
|
-
}
|
143
|
-
|
144
|
-
// This is to catch the JsTimeoutError we throw when we detect a timeout in
|
145
|
-
// a single JS execution.
|
146
|
-
if (error.code === JsTimeoutError.code) {
|
147
|
-
return JsTimeoutError.message
|
148
|
-
}
|
149
|
-
|
150
|
-
// This is to catch the error that happens if a user-supplied JS script
|
151
|
-
// throws for reasons introduced by the user.
|
152
|
-
if (error.code === UserScriptError.code) {
|
153
|
-
if (noThrow) {
|
154
|
-
return error.userScriptError.toString()
|
155
|
-
}
|
156
|
-
throw error
|
157
|
-
}
|
158
|
-
|
159
|
-
if (error.name === "SyntaxError") {
|
160
|
-
if (noThrow) {
|
161
|
-
return error.toString()
|
162
|
-
}
|
163
|
-
throw error
|
164
|
-
}
|
165
|
-
|
166
|
-
return "Error while executing JS"
|
167
|
-
}
|
168
|
-
}
|
package/src/helpers/list.ts
DELETED
@@ -1,72 +0,0 @@
|
|
1
|
-
import { date, duration } from "./date"
|
2
|
-
|
3
|
-
/*
|
4
|
-
@budibase/handlebars-helpers is not treeshakeable, so we can't use the barrel files.
|
5
|
-
Otherwise, we have issues when generating the isolated-vm bundle because of the treeshaking
|
6
|
-
*/
|
7
|
-
/* eslint-disable local-rules/no-budibase-imports */
|
8
|
-
// @ts-expect-error
|
9
|
-
import math from "@budibase/handlebars-helpers/lib/math"
|
10
|
-
// @ts-expect-error
|
11
|
-
import array from "@budibase/handlebars-helpers/lib/array"
|
12
|
-
// @ts-expect-error
|
13
|
-
import number from "@budibase/handlebars-helpers/lib/number"
|
14
|
-
// @ts-expect-error
|
15
|
-
import url from "@budibase/handlebars-helpers/lib/url"
|
16
|
-
// @ts-expect-error
|
17
|
-
import string from "@budibase/handlebars-helpers/lib/string"
|
18
|
-
// @ts-expect-error
|
19
|
-
import comparison from "@budibase/handlebars-helpers/lib/comparison"
|
20
|
-
// @ts-expect-error
|
21
|
-
import object from "@budibase/handlebars-helpers/lib/object"
|
22
|
-
// @ts-expect-error
|
23
|
-
import regex from "@budibase/handlebars-helpers/lib/regex"
|
24
|
-
// @ts-expect-error
|
25
|
-
import uuid from "@budibase/handlebars-helpers/lib/uuid"
|
26
|
-
/* eslint-enable local-rules/no-budibase-imports */
|
27
|
-
|
28
|
-
// https://github.com/evanw/esbuild/issues/56
|
29
|
-
const externalCollections = {
|
30
|
-
math,
|
31
|
-
array,
|
32
|
-
number,
|
33
|
-
url,
|
34
|
-
string,
|
35
|
-
comparison,
|
36
|
-
object,
|
37
|
-
regex,
|
38
|
-
uuid,
|
39
|
-
}
|
40
|
-
|
41
|
-
export const helpersToRemoveForJs = ["sortBy"]
|
42
|
-
|
43
|
-
const addedHelpers = {
|
44
|
-
date: date,
|
45
|
-
duration: duration,
|
46
|
-
}
|
47
|
-
|
48
|
-
let helpers: Record<string, any>
|
49
|
-
|
50
|
-
export function getJsHelperList() {
|
51
|
-
if (helpers) {
|
52
|
-
return helpers
|
53
|
-
}
|
54
|
-
|
55
|
-
helpers = {}
|
56
|
-
for (let collection of Object.values(externalCollections)) {
|
57
|
-
for (let [key, func] of Object.entries<any>(collection)) {
|
58
|
-
// Handlebars injects the hbs options to the helpers by default. We are adding an empty {} as a last parameter to simulate it
|
59
|
-
helpers[key] = (...props: any) => func(...props, {})
|
60
|
-
}
|
61
|
-
}
|
62
|
-
helpers = {
|
63
|
-
...helpers,
|
64
|
-
...addedHelpers,
|
65
|
-
}
|
66
|
-
|
67
|
-
for (const toRemove of helpersToRemoveForJs) {
|
68
|
-
delete helpers[toRemove]
|
69
|
-
}
|
70
|
-
Object.freeze(helpers)
|
71
|
-
return helpers
|
72
|
-
}
|
package/src/iife.ts
DELETED