mirror of
https://github.com/gohugoio/hugo.git
synced 2025-08-29 22:29:56 +02:00
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:
@@ -17,6 +17,7 @@ import (
|
||||
"errors"
|
||||
"html/template"
|
||||
"strings"
|
||||
texttemplate "text/template"
|
||||
"text/template/parse"
|
||||
)
|
||||
|
||||
@@ -35,32 +36,57 @@ var paramsPaths = [][]string{
|
||||
}
|
||||
|
||||
type templateContext struct {
|
||||
decl decl
|
||||
templ *template.Template
|
||||
visited map[string]bool
|
||||
decl decl
|
||||
visited map[string]bool
|
||||
lookupFn func(name string) *parse.Tree
|
||||
}
|
||||
|
||||
func (c templateContext) getIfNotVisited(name string) *template.Template {
|
||||
func (c templateContext) getIfNotVisited(name string) *parse.Tree {
|
||||
if c.visited[name] {
|
||||
return nil
|
||||
}
|
||||
c.visited[name] = true
|
||||
return c.templ.Lookup(name)
|
||||
return c.lookupFn(name)
|
||||
}
|
||||
|
||||
func newTemplateContext(templ *template.Template) *templateContext {
|
||||
return &templateContext{templ: templ, decl: make(map[string]string), visited: make(map[string]bool)}
|
||||
func newTemplateContext(lookupFn func(name string) *parse.Tree) *templateContext {
|
||||
return &templateContext{lookupFn: lookupFn, decl: make(map[string]string), visited: make(map[string]bool)}
|
||||
|
||||
}
|
||||
|
||||
func applyTemplateTransformers(templ *template.Template) error {
|
||||
if templ == nil || templ.Tree == nil {
|
||||
func createParseTreeLookup(templ *template.Template) func(nn string) *parse.Tree {
|
||||
return func(nn string) *parse.Tree {
|
||||
tt := templ.Lookup(nn)
|
||||
if tt != nil {
|
||||
return tt.Tree
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func applyTemplateTransformersToHMLTTemplate(templ *template.Template) error {
|
||||
return applyTemplateTransformers(templ.Tree, createParseTreeLookup(templ))
|
||||
}
|
||||
|
||||
func applyTemplateTransformersToTextTemplate(templ *texttemplate.Template) error {
|
||||
return applyTemplateTransformers(templ.Tree,
|
||||
func(nn string) *parse.Tree {
|
||||
tt := templ.Lookup(nn)
|
||||
if tt != nil {
|
||||
return tt.Tree
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func applyTemplateTransformers(templ *parse.Tree, lookupFn func(name string) *parse.Tree) error {
|
||||
if templ == nil {
|
||||
return errors.New("expected template, but none provided")
|
||||
}
|
||||
|
||||
c := newTemplateContext(templ)
|
||||
c := newTemplateContext(lookupFn)
|
||||
|
||||
c.paramsKeysToLower(templ.Tree.Root)
|
||||
c.paramsKeysToLower(templ.Root)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -84,7 +110,7 @@ func (c *templateContext) paramsKeysToLower(n parse.Node) {
|
||||
case *parse.TemplateNode:
|
||||
subTempl := c.getIfNotVisited(x.Name)
|
||||
if subTempl != nil {
|
||||
c.paramsKeysToLowerForNodes(subTempl.Tree.Root)
|
||||
c.paramsKeysToLowerForNodes(subTempl.Root)
|
||||
}
|
||||
case *parse.PipeNode:
|
||||
for i, elem := range x.Decl {
|
||||
|
Reference in New Issue
Block a user