Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[submodule "mustache"]
path = mustache
url = git://github.com/mustache/spec.git
url = https://github.com/mustache/spec
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1289,7 +1289,6 @@ These handlebars options are currently NOT implemented:
- `knownHelpers` - list of helpers that are known to exist (truthy) at template execution time
- `knownHelpersOnly` - allows further optimizations based on the known helpers list
- `trackIds` - include the id names used to resolve parameters for helpers
- `noEscape` - disables HTML escaping globally
- `strict` - templates will throw rather than silently ignore missing fields
- `assumeObjects` - removes object existence checks when traversing paths
- `preventIndent` - disables the auto-indententation of nested partials
Expand Down
5 changes: 4 additions & 1 deletion eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ type evalVisitor struct {

// used for info on panic
curNode ast.Node

// do not escape
noEscape bool
}

// NewEvalVisitor instanciate a new evaluation visitor with given context and initial private data frame
Expand Down Expand Up @@ -806,7 +809,7 @@ func (v *evalVisitor) VisitMustache(node *ast.MustacheStatement) interface{} {

// get string value
str := Str(expr)
if !isSafe && !node.Unescaped {
if !v.noEscape && !isSafe && !node.Unescaped {
// escape html
str = Escape(str)
}
Expand Down
16 changes: 16 additions & 0 deletions eval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,3 +295,19 @@ func TestEvalMethodReturningFunc(t *testing.T) {
t.Errorf("Failed to evaluate struct method: %s", output)
}
}

func TestEvalNoEscape(t *testing.T) {
t.Parallel()

source := `Hello {{value}}`
expected := `Hello <strong>World</strong>`

ctx := map[string]string{
"value": "<strong>World</strong>",
}

output := MustRender(source, ctx, WithNoEscape(true))
if output != expected {
t.Errorf("Failed to evaluate struct method: %s", output)
}
}
8 changes: 4 additions & 4 deletions handlebars.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ package handlebars
// Render parses a template and evaluates it with given context
//
// Note that this function call is not optimal as your template is parsed everytime you call it. You should use Parse() function instead.
func Render(source string, ctx interface{}) (string, error) {
func Render(source string, ctx interface{}, opts ...execOption) (string, error) {
// parse template
tpl, err := Parse(source)
if err != nil {
return "", err
}

// renders template
str, err := tpl.Exec(ctx)
str, err := tpl.Exec(ctx, opts...)
if err != nil {
return "", err
}
Expand All @@ -23,6 +23,6 @@ func Render(source string, ctx interface{}) (string, error) {
// MustRender parses a template and evaluates it with given context. It panics on error.
//
// Note that this function call is not optimal as your template is parsed everytime you call it. You should use Parse() function instead.
func MustRender(source string, ctx interface{}) string {
return MustParse(source).MustExec(ctx)
func MustRender(source string, ctx interface{}, opts ...execOption) string {
return MustParse(source).MustExec(ctx, opts...)
}
22 changes: 17 additions & 5 deletions template.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,21 +190,30 @@ func (tpl *Template) RegisterPartialTemplate(name string, template *Template) {
}

// Exec evaluates template with given context.
func (tpl *Template) Exec(ctx interface{}) (result string, err error) {
return tpl.ExecWith(ctx, nil)
func (tpl *Template) Exec(ctx interface{}, opts ...execOption) (result string, err error) {
return tpl.ExecWith(ctx, nil, opts...)
}

// MustExec evaluates template with given context. It panics on error.
func (tpl *Template) MustExec(ctx interface{}) string {
result, err := tpl.Exec(ctx)
func (tpl *Template) MustExec(ctx interface{}, opts ...execOption) string {
result, err := tpl.Exec(ctx, opts...)
if err != nil {
panic(err)
}
return result
}

type execOption func(*evalVisitor)

// Set noEscape option
func WithNoEscape(noEscape bool) func(*evalVisitor) {
return func(option *evalVisitor) {
option.noEscape = noEscape
}
}

// ExecWith evaluates template with given context and private data frame.
func (tpl *Template) ExecWith(ctx interface{}, privData *DataFrame) (result string, err error) {
func (tpl *Template) ExecWith(ctx interface{}, privData *DataFrame, opts ...execOption) (result string, err error) {
defer errRecover(&err)

// parses template if necessary
Expand All @@ -215,6 +224,9 @@ func (tpl *Template) ExecWith(ctx interface{}, privData *DataFrame) (result stri

// setup visitor
v := newEvalVisitor(tpl, ctx, privData)
for _, opt := range opts {
opt(v)
}

// visit AST
result, _ = tpl.program.Accept(v).(string)
Expand Down