mirror of
https://github.com/gohugoio/hugo.git
synced 2025-08-28 22:19:59 +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:
111
tpl/template.go
111
tpl/template.go
@@ -1,28 +1,103 @@
|
||||
// Copyright 2017-present The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tpl
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"io"
|
||||
|
||||
"text/template/parse"
|
||||
|
||||
"html/template"
|
||||
texttemplate "text/template"
|
||||
|
||||
bp "github.com/spf13/hugo/bufferpool"
|
||||
)
|
||||
|
||||
// TODO(bep) make smaller
|
||||
type Template interface {
|
||||
ExecuteTemplate(wr io.Writer, name string, data interface{}) error
|
||||
ExecuteTemplateToHTML(context interface{}, layouts ...string) template.HTML
|
||||
Lookup(name string) *template.Template
|
||||
Templates() []*template.Template
|
||||
New(name string) *template.Template
|
||||
GetClone() *template.Template
|
||||
RebuildClone() *template.Template
|
||||
LoadTemplates(absPath string)
|
||||
LoadTemplatesWithPrefix(absPath, prefix string)
|
||||
var (
|
||||
_ TemplateExecutor = (*TemplateAdapter)(nil)
|
||||
)
|
||||
|
||||
// TemplateHandler manages the collection of templates.
|
||||
type TemplateHandler interface {
|
||||
TemplateFinder
|
||||
AddTemplate(name, tpl string) error
|
||||
AddTemplateFileWithMaster(name, overlayFilename, masterFilename string) error
|
||||
AddAceTemplate(name, basePath, innerPath string, baseContent, innerContent []byte) error
|
||||
AddInternalTemplate(prefix, name, tpl string) error
|
||||
AddInternalShortcode(name, tpl string) error
|
||||
Partial(name string, contextList ...interface{}) template.HTML
|
||||
AddLateTemplate(name, tpl string) error
|
||||
LoadTemplates(absPath, prefix string)
|
||||
PrintErrors()
|
||||
Funcs(funcMap template.FuncMap)
|
||||
|
||||
MarkReady()
|
||||
RebuildClone()
|
||||
}
|
||||
|
||||
// TemplateFinder finds templates.
|
||||
type TemplateFinder interface {
|
||||
Lookup(name string) *TemplateAdapter
|
||||
}
|
||||
|
||||
// Template is the common interface between text/template and html/template.
|
||||
type Template interface {
|
||||
Execute(wr io.Writer, data interface{}) error
|
||||
Name() string
|
||||
}
|
||||
|
||||
// TemplateExecutor adds some extras to Template.
|
||||
type TemplateExecutor interface {
|
||||
Template
|
||||
ExecuteToString(data interface{}) (string, error)
|
||||
Tree() string
|
||||
}
|
||||
|
||||
// TemplateAdapter implements the TemplateExecutor interface.
|
||||
type TemplateAdapter struct {
|
||||
Template
|
||||
}
|
||||
|
||||
// ExecuteToString executes the current template and returns the result as a
|
||||
// string.
|
||||
func (t *TemplateAdapter) ExecuteToString(data interface{}) (string, error) {
|
||||
b := bp.GetBuffer()
|
||||
defer bp.PutBuffer(b)
|
||||
if err := t.Execute(b, data); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return b.String(), nil
|
||||
}
|
||||
|
||||
// Tree returns the template Parse tree as a string.
|
||||
// Note: this isn't safe for parallel execution on the same template
|
||||
// vs Lookup and Execute.
|
||||
func (t *TemplateAdapter) Tree() string {
|
||||
var tree *parse.Tree
|
||||
switch tt := t.Template.(type) {
|
||||
case *template.Template:
|
||||
tree = tt.Tree
|
||||
case *texttemplate.Template:
|
||||
tree = tt.Tree
|
||||
default:
|
||||
panic("Unknown template")
|
||||
}
|
||||
|
||||
if tree.Root == nil {
|
||||
return ""
|
||||
}
|
||||
s := tree.Root.String()
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// TemplateTestMocker adds a way to override some template funcs during tests.
|
||||
// The interface is named so it's not used in regular application code.
|
||||
type TemplateTestMocker interface {
|
||||
SetFuncs(funcMap map[string]interface{})
|
||||
}
|
||||
|
Reference in New Issue
Block a user