/
renderVelocityTemplateObject.js
93 lines (77 loc) · 2.75 KB
/
renderVelocityTemplateObject.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import { Compile, parse } from 'velocityjs'
import runInPollutedScope from '../javaHelpers.js'
import debugLog from '../../../debugLog.js'
import { isPlainObject } from '../../../utils/index.js'
const { entries } = Object
function tryToParseJSON(string) {
let parsed
try {
parsed = JSON.parse(string)
} catch (err) {
// nothing! Some things are not meant to be parsed.
}
return parsed || string
}
function renderVelocityString(velocityString, context) {
// runs in a "polluted" (extended) String.prototype replacement scope
const renderResult = runInPollutedScope(() =>
// This line can throw, but this function does not handle errors
// Quick args explanation:
// { escape: false } --> otherwise would escape &, < and > chars with html (&, < and >)
// render(context, null, true) --> null: no custom macros; true: silent mode, just like APIG
new Compile(parse(velocityString), { escape: false }).render(
context,
null,
true,
),
)
debugLog('Velocity rendered:', renderResult || 'undefined')
// Haaaa Velocity... this language sure loves strings a lot
switch (renderResult) {
case 'undefined':
return undefined // But we don't, we want JavaScript types
case 'null':
return null
case 'true':
return true
case 'false':
return false
default:
return tryToParseJSON(renderResult)
}
}
/*
Deeply traverses a Serverless-style JSON (Velocity) template
When it finds a string, assumes it's Velocity language and renders it.
*/
export default function renderVelocityTemplateObject(templateObject, context) {
const result = {}
let toProcess = templateObject
// In some projects, the template object is a string, let us see if it's JSON
if (typeof toProcess === 'string') {
toProcess = tryToParseJSON(toProcess)
}
// Let's check again
if (isPlainObject(toProcess)) {
entries(toProcess).forEach(([key, value]) => {
debugLog('Processing key:', key, '- value:', value)
if (typeof value === 'string') {
result[key] = renderVelocityString(value, context)
// Go deeper
} else if (isPlainObject(value)) {
result[key] = renderVelocityTemplateObject(value, context)
// This should never happen: value should either be a string or a plain object
} else {
result[key] = value
}
})
// Still a string? Maybe it's some complex Velocity stuff
} else if (typeof toProcess === 'string') {
// If the plugin threw here then you should consider reviewing your template or posting an issue.
const alternativeResult = tryToParseJSON(
renderVelocityString(toProcess, context),
)
return isPlainObject(alternativeResult) ? alternativeResult : result
}
return result
}