Skip to content

Commit 59fe95d

Browse files
committed
feat(Handlebars): custom escape functions
Some applications might need custom escape functions either because the output format is not HTML or because the application requires more strict HTML escaping.
1 parent 46a3152 commit 59fe95d

File tree

3 files changed

+275
-363
lines changed

3 files changed

+275
-363
lines changed

include/mrdocs/Support/Handlebars.hpp

+169-160
Original file line numberDiff line numberDiff line change
@@ -56,85 +56,6 @@ struct HandlebarsError
5656
, pos(pos_) {}
5757
};
5858

59-
/** Options for handlebars
60-
61-
In particular, we have the noHTMLEscape option, which we
62-
use to disable HTML escaping when rendering asciidoc templates.
63-
64-
This struct is analogous to the Handlebars.compile options.
65-
66-
*/
67-
struct HandlebarsOptions
68-
{
69-
/** Escape HTML entities
70-
*/
71-
bool noEscape = false;
72-
73-
/** Templates will throw rather than ignore missing fields
74-
75-
Run in strict mode. In this mode, templates will throw rather than
76-
silently ignore missing fields.
77-
78-
*/
79-
bool strict = false;
80-
81-
/** Removes object existence checks when traversing paths
82-
83-
This is a subset of strict mode that generates optimized
84-
templates when the data inputs are known to be safe.
85-
*/
86-
bool assumeObjects = false;
87-
88-
/** Disable the auto-indent feature
89-
90-
By default, an indented partial-call causes the output of the
91-
whole partial being indented by the same amount.
92-
*/
93-
bool preventIndent = false;
94-
95-
/** Disables standalone tag removal when set to true
96-
97-
When set, blocks and partials that are on their own line will not
98-
remove the whitespace on that line.
99-
*/
100-
bool ignoreStandalone = false;
101-
102-
/** Disables implicit context for partials
103-
104-
When enabled, partials that are not passed a context value will
105-
execute against an empty object.
106-
*/
107-
bool explicitPartialContext = false;
108-
109-
/** Enable recursive field lookup
110-
111-
When enabled, fields will be looked up recursively in objects
112-
and arrays.
113-
114-
This mode should be used to enable complete compatibility
115-
with Mustache templates.
116-
*/
117-
bool compat = false;
118-
119-
/** Enable tracking of ids
120-
121-
When enabled, the ids of the expressions are tracked and
122-
passed to the helpers.
123-
124-
Helpers often use this information to update the context
125-
path to the current expression, which can later be used to
126-
look up the value of the expression with ".." segments.
127-
*/
128-
bool trackIds = false;
129-
130-
/** Custom private data object
131-
132-
This variable can be used to pass in an object to define custom
133-
private variables.
134-
*/
135-
dom::Value data = nullptr;
136-
};
137-
13859
namespace detail {
13960
// Objects such as llvm::raw_string_ostream
14061
template <typename Os>
@@ -155,28 +76,6 @@ namespace detail {
15576
{
15677
st.append( sv.data(), sv.data() + sv.size() );
15778
};
158-
159-
struct RenderState;
160-
161-
// Heterogeneous lookup support
162-
struct string_hash {
163-
using is_transparent [[maybe_unused]] = void;
164-
size_t operator()(const char *txt) const {
165-
return std::hash<std::string_view>{}(txt);
166-
}
167-
size_t operator()(std::string_view txt) const {
168-
return std::hash<std::string_view>{}(txt);
169-
}
170-
size_t operator()(const std::string &txt) const {
171-
return std::hash<std::string>{}(txt);
172-
}
173-
};
174-
175-
using partials_map = std::unordered_map<
176-
std::string, std::string, string_hash, std::equal_to<>>;
177-
178-
using partials_view_map = std::unordered_map<
179-
std::string, std::string_view, string_hash, std::equal_to<>>;
18079
}
18180

18281
/** Reference to output stream used by handlebars
@@ -341,6 +240,172 @@ class MRDOCS_DECL OutputRef
341240
}
342241
};
343242

243+
/** HTML escapes the specified string
244+
245+
This function HTML escapes the specified string, making it safe for
246+
rendering as text within HTML content.
247+
248+
Replaces `&`, `<`, `>`, `"`, `'`, ```, `=` with the HTML entity
249+
equivalent value for string values.
250+
251+
The output of all expressions except for triple-braced expressions
252+
are passed through this method. Helpers should also use this method
253+
when returning HTML content via a SafeString instance, to prevent
254+
possible code injection.
255+
256+
Helper values created by the SafeString function are left untouched
257+
by the template and are not passed through this function.
258+
259+
This function has the same behavior as the corresponding utility function
260+
in the Handlebars.js library.
261+
262+
@see https://github.com/handlebars-lang/handlebars.js/blob/master/lib/handlebars/utils.js
263+
264+
@param out The output stream reference where the escaped string will be written.
265+
@param str The string to escape.
266+
*/
267+
MRDOCS_DECL
268+
void
269+
HTMLEscape(
270+
OutputRef& out,
271+
std::string_view str);
272+
273+
/** \brief HTML escapes the specified string.
274+
*
275+
* This function HTML escapes the specified string, making it safe for
276+
* rendering as text within HTML content.
277+
*
278+
* Replaces `&`, `<`, `>`, `"`, `'`, ```, `=` with the HTML entity
279+
* equivalent value for string values.
280+
*
281+
* The output of all expressions except for triple-braced expressions
282+
* are passed through this method. Helpers should also use this method
283+
* when returning HTML content via a SafeString instance, to prevent
284+
* possible code injection.
285+
*
286+
* Helper values created by the SafeString function are left untouched
287+
* by the template and are not passed through this function.
288+
*
289+
* \param str The string to escape.
290+
* \return The escaped string.
291+
*/
292+
MRDOCS_DECL
293+
std::string
294+
HTMLEscape(std::string_view str);
295+
296+
/** Options for handlebars
297+
298+
This struct is analogous to the Handlebars.compile options.
299+
300+
@see https://handlebarsjs.com/api-reference/compilation.html
301+
302+
*/
303+
struct HandlebarsOptions
304+
{
305+
/** Escape HTML entities or entities defined by the escape function
306+
*/
307+
bool noEscape = false;
308+
309+
/** Function to escape entities
310+
311+
It's initialized with a reference to the HTMLEscape function overload
312+
that takes an OutputRef and a string_view. This function can be
313+
replaced with a custom function that escapes entities in a different
314+
way.
315+
316+
*/
317+
std::function<void(OutputRef&, std::string_view)> escapeFunction =
318+
static_cast<void(*)(OutputRef&, std::string_view)>(HTMLEscape);
319+
320+
/** Templates will throw rather than ignore missing fields
321+
322+
Run in strict mode. In this mode, templates will throw rather than
323+
silently ignore missing fields.
324+
325+
*/
326+
bool strict = false;
327+
328+
/** Removes object existence checks when traversing paths
329+
330+
This is a subset of strict mode that generates optimized
331+
templates when the data inputs are known to be safe.
332+
*/
333+
bool assumeObjects = false;
334+
335+
/** Disable the auto-indent feature
336+
337+
By default, an indented partial-call causes the output of the
338+
whole partial being indented by the same amount.
339+
*/
340+
bool preventIndent = false;
341+
342+
/** Disables standalone tag removal when set to true
343+
344+
When set, blocks and partials that are on their own line will not
345+
remove the whitespace on that line.
346+
*/
347+
bool ignoreStandalone = false;
348+
349+
/** Disables implicit context for partials
350+
351+
When enabled, partials that are not passed a context value will
352+
execute against an empty object.
353+
*/
354+
bool explicitPartialContext = false;
355+
356+
/** Enable recursive field lookup
357+
358+
When enabled, fields will be looked up recursively in objects
359+
and arrays.
360+
361+
This mode should be used to enable complete compatibility
362+
with Mustache templates.
363+
*/
364+
bool compat = false;
365+
366+
/** Enable tracking of ids
367+
368+
When enabled, the ids of the expressions are tracked and
369+
passed to the helpers.
370+
371+
Helpers often use this information to update the context
372+
path to the current expression, which can later be used to
373+
look up the value of the expression with ".." segments.
374+
*/
375+
bool trackIds = false;
376+
377+
/** Custom private data object
378+
379+
This variable can be used to pass in an object to define custom
380+
private variables.
381+
*/
382+
dom::Value data = nullptr;
383+
};
384+
385+
namespace detail {
386+
struct RenderState;
387+
388+
// Heterogeneous lookup support
389+
struct string_hash {
390+
using is_transparent [[maybe_unused]] = void;
391+
size_t operator()(const char *txt) const {
392+
return std::hash<std::string_view>{}(txt);
393+
}
394+
size_t operator()(std::string_view txt) const {
395+
return std::hash<std::string_view>{}(txt);
396+
}
397+
size_t operator()(const std::string &txt) const {
398+
return std::hash<std::string>{}(txt);
399+
}
400+
};
401+
402+
using partials_map = std::unordered_map<
403+
std::string, std::string, string_hash, std::equal_to<>>;
404+
405+
using partials_view_map = std::unordered_map<
406+
std::string, std::string_view, string_hash, std::equal_to<>>;
407+
}
408+
344409
/** A handlebars environment
345410
346411
This class implements a handlebars template environment.
@@ -942,37 +1007,6 @@ createFrame(dom::Value const& parent);
9421007
dom::Object
9431008
createFrame(dom::Object const& child, dom::Object const& parent);
9441009

945-
/** HTML escapes the specified string
946-
947-
This function HTML escapes the specified string, making it safe for
948-
rendering as text within HTML content.
949-
950-
Replaces `&`, `<`, `>`, `"`, `'`, ```, `=` with the HTML entity
951-
equivalent value for string values.
952-
953-
The output of all expressions except for triple-braced expressions
954-
are passed through this method. Helpers should also use this method
955-
when returning HTML content via a SafeString instance, to prevent
956-
possible code injection.
957-
958-
Helper values created by the SafeString function are left untouched
959-
by the template and are not passed through this function.
960-
961-
@param str The string to escape
962-
@return The escaped string
963-
*/
964-
MRDOCS_DECL
965-
std::string
966-
escapeExpression(
967-
std::string_view str);
968-
969-
/// @overload escapeExpression(std::string_view)
970-
MRDOCS_DECL
971-
void
972-
escapeExpression(
973-
OutputRef out,
974-
std::string_view str);
975-
9761010
/// @overload escapeExpression(std::string_view)
9771011
MRDOCS_DECL
9781012
void
@@ -981,31 +1015,6 @@ escapeExpression(
9811015
std::string_view str,
9821016
HandlebarsOptions const& opt);
9831017

984-
template <std::convertible_to<dom::Value> DomValue>
985-
requires (!std::convertible_to<DomValue, std::string_view>)
986-
std::string
987-
escapeExpression(
988-
DomValue const& val)
989-
{
990-
dom::Value v = val;
991-
if (v.isString())
992-
{
993-
return escapeExpression(v.getString().get());
994-
}
995-
if (v.isObject() && v.getObject().exists("toHTML"))
996-
{
997-
dom::Value fn = v.getObject().get("toHTML");
998-
if (fn.isFunction()) {
999-
return toString(fn.getFunction()());
1000-
}
1001-
}
1002-
if (v.isNull() || v.isUndefined())
1003-
{
1004-
return {};
1005-
}
1006-
return toString(v);
1007-
}
1008-
10091018
namespace helpers {
10101019

10111020
/** Register all the built-in helpers into a Handlebars instance
@@ -1164,9 +1173,9 @@ not_fn(dom::Array const& arg);
11641173
MRDOCS_DECL
11651174
dom::Value
11661175
select_fn(
1167-
dom::Value condition,
1168-
dom::Value result_true,
1169-
dom::Value result_false);
1176+
const dom::Value& condition,
1177+
const dom::Value& result_true,
1178+
const dom::Value& result_false);
11701179

11711180
/** "increment" helper function
11721181
*

0 commit comments

Comments
 (0)