tpl: Rework to handle both text and HTML templates

Before this commit, Hugo used `html/template` for all Go templates.

While this is a fine choice for HTML and maybe also RSS feeds, it is painful for plain text formats such as CSV, JSON etc.

This commit fixes that by using the `IsPlainText` attribute on the output format to decide what to use.

A couple of notes:

* The above requires a nonambiguous template name to type mapping. I.e. `/layouts/_default/list.json` will only work if there is only one JSON output format, `/layouts/_default/list.mytype.json` will always work.
* Ambiguous types will fall back to HTML.
* Partials inherits the text vs HTML identificator of the container template. This also means that plain text templates can only include plain text partials.
* Shortcode templates are, by definition, currently HTML templates only.

Fixes #3221
This commit is contained in:
Bjørn Erik Pedersen
2017-03-27 20:43:49 +02:00
parent 73c1c7b69d
commit 5c5efa03d2
31 changed files with 1208 additions and 840 deletions

View File

@@ -281,8 +281,8 @@ urlize: bat-man
v.Set("CurrentContentLanguage", helpers.NewLanguage("en", v))
config := newDepsConfig(v)
config.WithTemplate = func(templ tpl.Template) error {
if _, err := templ.New("test").Parse(in); err != nil {
config.WithTemplate = func(templ tpl.TemplateHandler) error {
if err := templ.AddTemplate("test", in); err != nil {
t.Fatal("Got error on parse", err)
}
return nil
@@ -2858,6 +2858,56 @@ func TestReadFile(t *testing.T) {
}
}
func TestPartialHTMLAndText(t *testing.T) {
t.Parallel()
config := newDepsConfig(viper.New())
data := struct {
Name string
}{
Name: "a+b+c", // This should get encoded in HTML.
}
config.WithTemplate = func(templ tpl.TemplateHandler) error {
if err := templ.AddTemplate("htmlTemplate.html", `HTML Test Partial: {{ partial "test.foo" . -}}`); err != nil {
return err
}
if err := templ.AddTemplate("_text/textTemplate.txt", `Text Test Partial: {{ partial "test.foo" . -}}`); err != nil {
return err
}
// Use "foo" here to say that the extension doesn't really matter in this scenario.
// It will look for templates in "partials/test.foo" and "partials/test.foo.html".
if err := templ.AddTemplate("partials/test.foo", "HTML Name: {{ .Name }}"); err != nil {
return err
}
if err := templ.AddTemplate("_text/partials/test.foo", "Text Name: {{ .Name }}"); err != nil {
return err
}
return nil
}
de, err := deps.New(config)
require.NoError(t, err)
require.NoError(t, de.LoadResources())
templ := de.Tmpl.Lookup("htmlTemplate.html")
require.NotNil(t, templ)
resultHTML, err := templ.ExecuteToString(data)
require.NoError(t, err)
templ = de.Tmpl.Lookup("_text/textTemplate.txt")
require.NotNil(t, templ)
resultText, err := templ.ExecuteToString(data)
require.NoError(t, err)
require.Contains(t, resultHTML, "HTML Test Partial: HTML Name: a+b+c")
require.Contains(t, resultText, "Text Test Partial: Text Name: a+b+c")
}
func TestPartialCached(t *testing.T) {
t.Parallel()
testCases := []struct {
@@ -2893,7 +2943,7 @@ func TestPartialCached(t *testing.T) {
config := newDepsConfig(viper.New())
config.WithTemplate = func(templ tpl.Template) error {
config.WithTemplate = func(templ tpl.TemplateHandler) error {
err := templ.AddTemplate("testroot", tmp)
if err != nil {
return err
@@ -2933,7 +2983,7 @@ func TestPartialCached(t *testing.T) {
func BenchmarkPartial(b *testing.B) {
config := newDepsConfig(viper.New())
config.WithTemplate = func(templ tpl.Template) error {
config.WithTemplate = func(templ tpl.TemplateHandler) error {
err := templ.AddTemplate("testroot", `{{ partial "bench1" . }}`)
if err != nil {
return err
@@ -2965,7 +3015,7 @@ func BenchmarkPartial(b *testing.B) {
func BenchmarkPartialCached(b *testing.B) {
config := newDepsConfig(viper.New())
config.WithTemplate = func(templ tpl.Template) error {
config.WithTemplate = func(templ tpl.TemplateHandler) error {
err := templ.AddTemplate("testroot", `{{ partialCached "bench1" . }}`)
if err != nil {
return err
@@ -3010,12 +3060,12 @@ func newTestFuncsterWithViper(v *viper.Viper) *templateFuncster {
panic(err)
}
return d.Tmpl.(*GoHTMLTemplate).funcster
return d.Tmpl.(*templateHandler).html.funcster
}
func newTestTemplate(t *testing.T, name, template string) *template.Template {
func newTestTemplate(t *testing.T, name, template string) tpl.Template {
config := newDepsConfig(viper.New())
config.WithTemplate = func(templ tpl.Template) error {
config.WithTemplate = func(templ tpl.TemplateHandler) error {
err := templ.AddTemplate(name, template)
if err != nil {
return err