mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-07 20:30:36 -05:00
tpl: Refactor package
Now: * The template API lives in /tpl * The rest lives in /tpl/tplimpl This is bound te be more improved in the future. Updates #2701
This commit is contained in:
parent
93ca7c9e95
commit
c507e2717d
23 changed files with 661 additions and 662 deletions
8
deps/deps.go
vendored
8
deps/deps.go
vendored
|
@ -8,7 +8,7 @@ import (
|
|||
"github.com/spf13/hugo/config"
|
||||
"github.com/spf13/hugo/helpers"
|
||||
"github.com/spf13/hugo/hugofs"
|
||||
"github.com/spf13/hugo/tplapi"
|
||||
"github.com/spf13/hugo/tpl"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
|
||||
|
@ -20,7 +20,7 @@ type Deps struct {
|
|||
Log *jww.Notepad `json:"-"`
|
||||
|
||||
// The templates to use.
|
||||
Tmpl tplapi.Template `json:"-"`
|
||||
Tmpl tpl.Template `json:"-"`
|
||||
|
||||
// The file systems to use.
|
||||
Fs *hugofs.Fs `json:"-"`
|
||||
|
@ -40,7 +40,7 @@ type Deps struct {
|
|||
Language *helpers.Language
|
||||
|
||||
templateProvider ResourceProvider
|
||||
WithTemplate func(templ tplapi.Template) error `json:"-"`
|
||||
WithTemplate func(templ tpl.Template) error `json:"-"`
|
||||
|
||||
translationProvider ResourceProvider
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ type DepsCfg struct {
|
|||
|
||||
// Template handling.
|
||||
TemplateProvider ResourceProvider
|
||||
WithTemplate func(templ tplapi.Template) error
|
||||
WithTemplate func(templ tpl.Template) error
|
||||
|
||||
// i18n handling.
|
||||
TranslationProvider ResourceProvider
|
||||
|
|
|
@ -25,7 +25,7 @@ import (
|
|||
"github.com/spf13/hugo/deps"
|
||||
|
||||
"github.com/spf13/hugo/helpers"
|
||||
"github.com/spf13/hugo/tplapi"
|
||||
"github.com/spf13/hugo/tpl"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -335,7 +335,7 @@ func TestShortcodeTweet(t *testing.T) {
|
|||
th = testHelper{cfg}
|
||||
)
|
||||
|
||||
withTemplate := func(templ tplapi.Template) error {
|
||||
withTemplate := func(templ tpl.Template) error {
|
||||
templ.Funcs(tweetFuncMap)
|
||||
return nil
|
||||
}
|
||||
|
@ -390,7 +390,7 @@ func TestShortcodeInstagram(t *testing.T) {
|
|||
th = testHelper{cfg}
|
||||
)
|
||||
|
||||
withTemplate := func(templ tplapi.Template) error {
|
||||
withTemplate := func(templ tpl.Template) error {
|
||||
templ.Funcs(instagramFuncMap)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ import (
|
|||
|
||||
"github.com/spf13/hugo/i18n"
|
||||
"github.com/spf13/hugo/tpl"
|
||||
"github.com/spf13/hugo/tplapi"
|
||||
"github.com/spf13/hugo/tpl/tplimpl"
|
||||
)
|
||||
|
||||
// HugoSites represents the sites to build. Each site represents a language.
|
||||
|
@ -72,7 +72,7 @@ func newHugoSites(cfg deps.DepsCfg, sites ...*Site) (*HugoSites, error) {
|
|||
|
||||
func applyDepsIfNeeded(cfg deps.DepsCfg, sites ...*Site) error {
|
||||
if cfg.TemplateProvider == nil {
|
||||
cfg.TemplateProvider = tpl.DefaultTemplateProvider
|
||||
cfg.TemplateProvider = tplimpl.DefaultTemplateProvider
|
||||
}
|
||||
|
||||
if cfg.TranslationProvider == nil {
|
||||
|
@ -121,8 +121,8 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) {
|
|||
return newHugoSites(cfg, sites...)
|
||||
}
|
||||
|
||||
func (s *Site) withSiteTemplates(withTemplates ...func(templ tplapi.Template) error) func(templ tplapi.Template) error {
|
||||
return func(templ tplapi.Template) error {
|
||||
func (s *Site) withSiteTemplates(withTemplates ...func(templ tpl.Template) error) func(templ tpl.Template) error {
|
||||
return func(templ tpl.Template) error {
|
||||
templ.LoadTemplates(s.absLayoutDir())
|
||||
if s.hasTheme() {
|
||||
templ.LoadTemplatesWithPrefix(s.absThemeDir()+"/layouts", "theme")
|
||||
|
@ -191,7 +191,7 @@ func (h *HugoSites) reset() {
|
|||
h.Sites[i] = s.reset()
|
||||
}
|
||||
|
||||
tpl.ResetCaches()
|
||||
tplimpl.ResetCaches()
|
||||
}
|
||||
|
||||
func (h *HugoSites) createSitesFromConfig() error {
|
||||
|
@ -553,7 +553,7 @@ func (h *HugoSites) Pages() Pages {
|
|||
return h.Sites[0].AllPages
|
||||
}
|
||||
|
||||
func handleShortcodes(p *Page, t tplapi.Template, rawContentCopy []byte) ([]byte, error) {
|
||||
func handleShortcodes(p *Page, t tpl.Template, rawContentCopy []byte) ([]byte, error) {
|
||||
if len(p.contentShortCodes) > 0 {
|
||||
p.s.Log.DEBUG.Printf("Replace %d shortcodes in %q", len(p.contentShortCodes), p.BaseFileName())
|
||||
shortcodes, err := executeShortcodeFuncMap(p.contentShortCodes)
|
||||
|
|
|
@ -26,7 +26,7 @@ import (
|
|||
|
||||
bp "github.com/spf13/hugo/bufferpool"
|
||||
"github.com/spf13/hugo/helpers"
|
||||
"github.com/spf13/hugo/tplapi"
|
||||
"github.com/spf13/hugo/tpl"
|
||||
)
|
||||
|
||||
// ShortcodeWithPage is the "." context in a shortcode template.
|
||||
|
@ -541,7 +541,7 @@ func replaceShortcodeTokens(source []byte, prefix string, replacements map[strin
|
|||
return source, nil
|
||||
}
|
||||
|
||||
func getShortcodeTemplate(name string, t tplapi.Template) *template.Template {
|
||||
func getShortcodeTemplate(name string, t tpl.Template) *template.Template {
|
||||
if x := t.Lookup("shortcodes/" + name + ".html"); x != nil {
|
||||
return x
|
||||
}
|
||||
|
|
|
@ -25,12 +25,12 @@ import (
|
|||
"github.com/spf13/hugo/deps"
|
||||
"github.com/spf13/hugo/helpers"
|
||||
"github.com/spf13/hugo/source"
|
||||
"github.com/spf13/hugo/tplapi"
|
||||
"github.com/spf13/hugo/tpl"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TODO(bep) remove
|
||||
func pageFromString(in, filename string, withTemplate ...func(templ tplapi.Template) error) (*Page, error) {
|
||||
func pageFromString(in, filename string, withTemplate ...func(templ tpl.Template) error) (*Page, error) {
|
||||
s := newTestSite(nil)
|
||||
if len(withTemplate) > 0 {
|
||||
// Have to create a new site
|
||||
|
@ -47,11 +47,11 @@ func pageFromString(in, filename string, withTemplate ...func(templ tplapi.Templ
|
|||
return s.NewPageFrom(strings.NewReader(in), filename)
|
||||
}
|
||||
|
||||
func CheckShortCodeMatch(t *testing.T, input, expected string, withTemplate func(templ tplapi.Template) error) {
|
||||
func CheckShortCodeMatch(t *testing.T, input, expected string, withTemplate func(templ tpl.Template) error) {
|
||||
CheckShortCodeMatchAndError(t, input, expected, withTemplate, false)
|
||||
}
|
||||
|
||||
func CheckShortCodeMatchAndError(t *testing.T, input, expected string, withTemplate func(templ tplapi.Template) error, expectError bool) {
|
||||
func CheckShortCodeMatchAndError(t *testing.T, input, expected string, withTemplate func(templ tpl.Template) error, expectError bool) {
|
||||
|
||||
cfg, fs := newTestCfg()
|
||||
|
||||
|
@ -100,7 +100,7 @@ func TestNonSC(t *testing.T) {
|
|||
// Issue #929
|
||||
func TestHyphenatedSC(t *testing.T) {
|
||||
t.Parallel()
|
||||
wt := func(tem tplapi.Template) error {
|
||||
wt := func(tem tpl.Template) error {
|
||||
tem.AddInternalShortcode("hyphenated-video.html", `Playing Video {{ .Get 0 }}`)
|
||||
return nil
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ func TestHyphenatedSC(t *testing.T) {
|
|||
// Issue #1753
|
||||
func TestNoTrailingNewline(t *testing.T) {
|
||||
t.Parallel()
|
||||
wt := func(tem tplapi.Template) error {
|
||||
wt := func(tem tpl.Template) error {
|
||||
tem.AddInternalShortcode("a.html", `{{ .Get 0 }}`)
|
||||
return nil
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ func TestNoTrailingNewline(t *testing.T) {
|
|||
|
||||
func TestPositionalParamSC(t *testing.T) {
|
||||
t.Parallel()
|
||||
wt := func(tem tplapi.Template) error {
|
||||
wt := func(tem tpl.Template) error {
|
||||
tem.AddInternalShortcode("video.html", `Playing Video {{ .Get 0 }}`)
|
||||
return nil
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ func TestPositionalParamSC(t *testing.T) {
|
|||
|
||||
func TestPositionalParamIndexOutOfBounds(t *testing.T) {
|
||||
t.Parallel()
|
||||
wt := func(tem tplapi.Template) error {
|
||||
wt := func(tem tpl.Template) error {
|
||||
tem.AddInternalShortcode("video.html", `Playing Video {{ .Get 1 }}`)
|
||||
return nil
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ func TestPositionalParamIndexOutOfBounds(t *testing.T) {
|
|||
|
||||
func TestNamedParamSC(t *testing.T) {
|
||||
t.Parallel()
|
||||
wt := func(tem tplapi.Template) error {
|
||||
wt := func(tem tpl.Template) error {
|
||||
tem.AddInternalShortcode("img.html", `<img{{ with .Get "src" }} src="{{.}}"{{end}}{{with .Get "class"}} class="{{.}}"{{end}}>`)
|
||||
return nil
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ func TestNamedParamSC(t *testing.T) {
|
|||
// Issue #2294
|
||||
func TestNestedNamedMissingParam(t *testing.T) {
|
||||
t.Parallel()
|
||||
wt := func(tem tplapi.Template) error {
|
||||
wt := func(tem tpl.Template) error {
|
||||
tem.AddInternalShortcode("acc.html", `<div class="acc">{{ .Inner }}</div>`)
|
||||
tem.AddInternalShortcode("div.html", `<div {{with .Get "class"}} class="{{ . }}"{{ end }}>{{ .Inner }}</div>`)
|
||||
tem.AddInternalShortcode("div2.html", `<div {{with .Get 0}} class="{{ . }}"{{ end }}>{{ .Inner }}</div>`)
|
||||
|
@ -174,7 +174,7 @@ func TestNestedNamedMissingParam(t *testing.T) {
|
|||
|
||||
func TestIsNamedParamsSC(t *testing.T) {
|
||||
t.Parallel()
|
||||
wt := func(tem tplapi.Template) error {
|
||||
wt := func(tem tpl.Template) error {
|
||||
tem.AddInternalShortcode("byposition.html", `<div id="{{ .Get 0 }}">`)
|
||||
tem.AddInternalShortcode("byname.html", `<div id="{{ .Get "id" }}">`)
|
||||
tem.AddInternalShortcode("ifnamedparams.html", `<div id="{{ if .IsNamedParams }}{{ .Get "id" }}{{ else }}{{ .Get 0 }}{{end}}">`)
|
||||
|
@ -190,7 +190,7 @@ func TestIsNamedParamsSC(t *testing.T) {
|
|||
|
||||
func TestInnerSC(t *testing.T) {
|
||||
t.Parallel()
|
||||
wt := func(tem tplapi.Template) error {
|
||||
wt := func(tem tpl.Template) error {
|
||||
tem.AddInternalShortcode("inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`)
|
||||
return nil
|
||||
}
|
||||
|
@ -201,7 +201,7 @@ func TestInnerSC(t *testing.T) {
|
|||
|
||||
func TestInnerSCWithMarkdown(t *testing.T) {
|
||||
t.Parallel()
|
||||
wt := func(tem tplapi.Template) error {
|
||||
wt := func(tem tpl.Template) error {
|
||||
tem.AddInternalShortcode("inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`)
|
||||
return nil
|
||||
}
|
||||
|
@ -215,7 +215,7 @@ func TestInnerSCWithMarkdown(t *testing.T) {
|
|||
|
||||
func TestInnerSCWithAndWithoutMarkdown(t *testing.T) {
|
||||
t.Parallel()
|
||||
wt := func(tem tplapi.Template) error {
|
||||
wt := func(tem tpl.Template) error {
|
||||
tem.AddInternalShortcode("inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`)
|
||||
return nil
|
||||
}
|
||||
|
@ -246,7 +246,7 @@ func TestEmbeddedSC(t *testing.T) {
|
|||
|
||||
func TestNestedSC(t *testing.T) {
|
||||
t.Parallel()
|
||||
wt := func(tem tplapi.Template) error {
|
||||
wt := func(tem tpl.Template) error {
|
||||
tem.AddInternalShortcode("scn1.html", `<div>Outer, inner is {{ .Inner }}</div>`)
|
||||
tem.AddInternalShortcode("scn2.html", `<div>SC2</div>`)
|
||||
return nil
|
||||
|
@ -258,7 +258,7 @@ func TestNestedSC(t *testing.T) {
|
|||
|
||||
func TestNestedComplexSC(t *testing.T) {
|
||||
t.Parallel()
|
||||
wt := func(tem tplapi.Template) error {
|
||||
wt := func(tem tpl.Template) error {
|
||||
tem.AddInternalShortcode("row.html", `-row-{{ .Inner}}-rowStop-`)
|
||||
tem.AddInternalShortcode("column.html", `-col-{{.Inner }}-colStop-`)
|
||||
tem.AddInternalShortcode("aside.html", `-aside-{{ .Inner }}-asideStop-`)
|
||||
|
@ -274,7 +274,7 @@ func TestNestedComplexSC(t *testing.T) {
|
|||
|
||||
func TestParentShortcode(t *testing.T) {
|
||||
t.Parallel()
|
||||
wt := func(tem tplapi.Template) error {
|
||||
wt := func(tem tpl.Template) error {
|
||||
tem.AddInternalShortcode("r1.html", `1: {{ .Get "pr1" }} {{ .Inner }}`)
|
||||
tem.AddInternalShortcode("r2.html", `2: {{ .Parent.Get "pr1" }}{{ .Get "pr2" }} {{ .Inner }}`)
|
||||
tem.AddInternalShortcode("r3.html", `3: {{ .Parent.Parent.Get "pr1" }}{{ .Parent.Get "pr2" }}{{ .Get "pr3" }} {{ .Inner }}`)
|
||||
|
@ -342,7 +342,7 @@ func TestExtractShortcodes(t *testing.T) {
|
|||
fmt.Sprintf("Hello %sworld%s. And that's it.", testScPlaceholderRegexp, testScPlaceholderRegexp), ""},
|
||||
} {
|
||||
|
||||
p, _ := pageFromString(simplePage, "simple.md", func(templ tplapi.Template) error {
|
||||
p, _ := pageFromString(simplePage, "simple.md", func(templ tpl.Template) error {
|
||||
templ.AddInternalShortcode("tag.html", `tag`)
|
||||
templ.AddInternalShortcode("sc1.html", `sc1`)
|
||||
templ.AddInternalShortcode("sc2.html", `sc2`)
|
||||
|
@ -514,7 +514,7 @@ tags:
|
|||
sources[i] = source.ByteSource{Name: filepath.FromSlash(test.contentPath), Content: []byte(test.content)}
|
||||
}
|
||||
|
||||
addTemplates := func(templ tplapi.Template) error {
|
||||
addTemplates := func(templ tpl.Template) error {
|
||||
templ.AddTemplate("_default/single.html", "{{.Content}}")
|
||||
|
||||
templ.AddInternalShortcode("b.html", `b`)
|
||||
|
|
|
@ -40,7 +40,7 @@ import (
|
|||
"github.com/spf13/hugo/parser"
|
||||
"github.com/spf13/hugo/source"
|
||||
"github.com/spf13/hugo/target"
|
||||
"github.com/spf13/hugo/tplapi"
|
||||
"github.com/spf13/hugo/tpl"
|
||||
"github.com/spf13/hugo/transform"
|
||||
"github.com/spf13/nitro"
|
||||
"github.com/spf13/viper"
|
||||
|
@ -149,7 +149,7 @@ func NewSite(cfg deps.DepsCfg) (*Site, error) {
|
|||
// NewSiteDefaultLang creates a new site in the default language.
|
||||
// The site will have a template system loaded and ready to use.
|
||||
// Note: This is mainly used in single site tests.
|
||||
func NewSiteDefaultLang(withTemplate ...func(templ tplapi.Template) error) (*Site, error) {
|
||||
func NewSiteDefaultLang(withTemplate ...func(templ tpl.Template) error) (*Site, error) {
|
||||
v := viper.New()
|
||||
loadDefaultSettingsFor(v)
|
||||
return newSiteForLang(helpers.NewDefaultLanguage(v), withTemplate...)
|
||||
|
@ -158,15 +158,15 @@ func NewSiteDefaultLang(withTemplate ...func(templ tplapi.Template) error) (*Sit
|
|||
// NewEnglishSite creates a new site in English language.
|
||||
// The site will have a template system loaded and ready to use.
|
||||
// Note: This is mainly used in single site tests.
|
||||
func NewEnglishSite(withTemplate ...func(templ tplapi.Template) error) (*Site, error) {
|
||||
func NewEnglishSite(withTemplate ...func(templ tpl.Template) error) (*Site, error) {
|
||||
v := viper.New()
|
||||
loadDefaultSettingsFor(v)
|
||||
return newSiteForLang(helpers.NewLanguage("en", v), withTemplate...)
|
||||
}
|
||||
|
||||
// newSiteForLang creates a new site in the given language.
|
||||
func newSiteForLang(lang *helpers.Language, withTemplate ...func(templ tplapi.Template) error) (*Site, error) {
|
||||
withTemplates := func(templ tplapi.Template) error {
|
||||
func newSiteForLang(lang *helpers.Language, withTemplate ...func(templ tpl.Template) error) (*Site, error) {
|
||||
withTemplates := func(templ tpl.Template) error {
|
||||
for _, wt := range withTemplate {
|
||||
if err := wt(templ); err != nil {
|
||||
return err
|
||||
|
|
|
@ -19,7 +19,7 @@ import (
|
|||
"reflect"
|
||||
|
||||
"github.com/spf13/hugo/deps"
|
||||
"github.com/spf13/hugo/tplapi"
|
||||
"github.com/spf13/hugo/tpl"
|
||||
)
|
||||
|
||||
const sitemapTemplate = `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
|
@ -48,7 +48,7 @@ func doTestSitemapOutput(t *testing.T, internal bool) {
|
|||
depsCfg := deps.DepsCfg{Fs: fs, Cfg: cfg}
|
||||
|
||||
if !internal {
|
||||
depsCfg.WithTemplate = func(templ tplapi.Template) error {
|
||||
depsCfg.WithTemplate = func(templ tpl.Template) error {
|
||||
templ.AddTemplate("sitemap.xml", sitemapTemplate)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"github.com/spf13/hugo/deps"
|
||||
"github.com/spf13/hugo/helpers"
|
||||
"github.com/spf13/hugo/source"
|
||||
"github.com/spf13/hugo/tplapi"
|
||||
"github.com/spf13/hugo/tpl"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"io/ioutil"
|
||||
|
@ -66,9 +66,9 @@ func newDebugLogger() *jww.Notepad {
|
|||
return jww.NewNotepad(jww.LevelDebug, jww.LevelError, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
|
||||
}
|
||||
|
||||
func createWithTemplateFromNameValues(additionalTemplates ...string) func(templ tplapi.Template) error {
|
||||
func createWithTemplateFromNameValues(additionalTemplates ...string) func(templ tpl.Template) error {
|
||||
|
||||
return func(templ tplapi.Template) error {
|
||||
return func(templ tpl.Template) error {
|
||||
for i := 0; i < len(additionalTemplates); i += 2 {
|
||||
err := templ.AddTemplate(additionalTemplates[i], additionalTemplates[i+1])
|
||||
if err != nil {
|
||||
|
|
586
tpl/template.go
586
tpl/template.go
|
@ -1,575 +1,27 @@
|
|||
// Copyright 2016 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 (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"sync"
|
||||
|
||||
"github.com/eknkc/amber"
|
||||
"github.com/spf13/afero"
|
||||
bp "github.com/spf13/hugo/bufferpool"
|
||||
"github.com/spf13/hugo/deps"
|
||||
"github.com/spf13/hugo/helpers"
|
||||
"github.com/yosssi/ace"
|
||||
)
|
||||
|
||||
// TODO(bep) globals get rid of the rest of the jww.ERR etc.
|
||||
|
||||
// Protecting global map access (Amber)
|
||||
var amberMu sync.Mutex
|
||||
|
||||
type templateErr struct {
|
||||
name string
|
||||
err error
|
||||
}
|
||||
|
||||
type GoHTMLTemplate struct {
|
||||
*template.Template
|
||||
|
||||
clone *template.Template
|
||||
|
||||
// a separate storage for the overlays created from cloned master templates.
|
||||
// note: No mutex protection, so we add these in one Go routine, then just read.
|
||||
overlays map[string]*template.Template
|
||||
|
||||
errors []*templateErr
|
||||
|
||||
funcster *templateFuncster
|
||||
|
||||
amberFuncMap template.FuncMap
|
||||
|
||||
*deps.Deps
|
||||
}
|
||||
|
||||
type TemplateProvider struct{}
|
||||
|
||||
var DefaultTemplateProvider *TemplateProvider
|
||||
|
||||
// Update updates the Hugo Template System in the provided Deps.
|
||||
// with all the additional features, templates & functions
|
||||
func (*TemplateProvider) Update(deps *deps.Deps) error {
|
||||
// TODO(bep) check that this isn't called too many times.
|
||||
tmpl := &GoHTMLTemplate{
|
||||
Template: template.New(""),
|
||||
overlays: make(map[string]*template.Template),
|
||||
errors: make([]*templateErr, 0),
|
||||
Deps: deps,
|
||||
}
|
||||
|
||||
deps.Tmpl = tmpl
|
||||
|
||||
tmpl.initFuncs(deps)
|
||||
|
||||
tmpl.LoadEmbedded()
|
||||
|
||||
if deps.WithTemplate != nil {
|
||||
err := deps.WithTemplate(tmpl)
|
||||
if err != nil {
|
||||
tmpl.errors = append(tmpl.errors, &templateErr{"init", err})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
tmpl.MarkReady()
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// Clone clones
|
||||
func (*TemplateProvider) Clone(d *deps.Deps) error {
|
||||
|
||||
t := d.Tmpl.(*GoHTMLTemplate)
|
||||
|
||||
// 1. Clone the clone with new template funcs
|
||||
// 2. Clone any overlays with new template funcs
|
||||
|
||||
tmpl := &GoHTMLTemplate{
|
||||
Template: template.Must(t.Template.Clone()),
|
||||
overlays: make(map[string]*template.Template),
|
||||
errors: make([]*templateErr, 0),
|
||||
Deps: d,
|
||||
}
|
||||
|
||||
d.Tmpl = tmpl
|
||||
tmpl.initFuncs(d)
|
||||
|
||||
for k, v := range t.overlays {
|
||||
vc := template.Must(v.Clone())
|
||||
// The extra lookup is a workaround, see
|
||||
// * https://github.com/golang/go/issues/16101
|
||||
// * https://github.com/spf13/hugo/issues/2549
|
||||
vc = vc.Lookup(vc.Name())
|
||||
vc.Funcs(tmpl.funcster.funcMap)
|
||||
tmpl.overlays[k] = vc
|
||||
}
|
||||
|
||||
tmpl.MarkReady()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) initFuncs(d *deps.Deps) {
|
||||
|
||||
t.funcster = newTemplateFuncster(d)
|
||||
|
||||
// The URL funcs in the funcMap is somewhat language dependent,
|
||||
// so we need to wait until the language and site config is loaded.
|
||||
t.funcster.initFuncMap()
|
||||
|
||||
t.amberFuncMap = template.FuncMap{}
|
||||
|
||||
amberMu.Lock()
|
||||
for k, v := range amber.FuncMap {
|
||||
t.amberFuncMap[k] = v
|
||||
}
|
||||
|
||||
for k, v := range t.funcster.funcMap {
|
||||
t.amberFuncMap[k] = v
|
||||
// Hacky, but we need to make sure that the func names are in the global map.
|
||||
amber.FuncMap[k] = func() string {
|
||||
panic("should never be invoked")
|
||||
}
|
||||
}
|
||||
amberMu.Unlock()
|
||||
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) Funcs(funcMap template.FuncMap) {
|
||||
t.Template.Funcs(funcMap)
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) Partial(name string, contextList ...interface{}) template.HTML {
|
||||
if strings.HasPrefix("partials/", name) {
|
||||
name = name[8:]
|
||||
}
|
||||
var context interface{}
|
||||
|
||||
if len(contextList) == 0 {
|
||||
context = nil
|
||||
} else {
|
||||
context = contextList[0]
|
||||
}
|
||||
return t.ExecuteTemplateToHTML(context, "partials/"+name, "theme/partials/"+name)
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) executeTemplate(context interface{}, w io.Writer, layouts ...string) {
|
||||
var worked bool
|
||||
for _, layout := range layouts {
|
||||
templ := t.Lookup(layout)
|
||||
if templ == nil {
|
||||
layout += ".html"
|
||||
templ = t.Lookup(layout)
|
||||
}
|
||||
|
||||
if templ != nil {
|
||||
if err := templ.Execute(w, context); err != nil {
|
||||
helpers.DistinctErrorLog.Println(layout, err)
|
||||
}
|
||||
worked = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !worked {
|
||||
t.Log.ERROR.Println("Unable to render", layouts)
|
||||
t.Log.ERROR.Println("Expecting to find a template in either the theme/layouts or /layouts in one of the following relative locations", layouts)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) ExecuteTemplateToHTML(context interface{}, layouts ...string) template.HTML {
|
||||
b := bp.GetBuffer()
|
||||
defer bp.PutBuffer(b)
|
||||
t.executeTemplate(context, b, layouts...)
|
||||
return template.HTML(b.String())
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) Lookup(name string) *template.Template {
|
||||
|
||||
if templ := t.Template.Lookup(name); templ != nil {
|
||||
return templ
|
||||
}
|
||||
|
||||
if t.overlays != nil {
|
||||
if templ, ok := t.overlays[name]; ok {
|
||||
return templ
|
||||
}
|
||||
}
|
||||
|
||||
// The clone is used for the non-renderable HTML pages (p.IsRenderable == false) that is parsed
|
||||
// as Go templates late in the build process.
|
||||
if t.clone != nil {
|
||||
if templ := t.clone.Lookup(name); templ != nil {
|
||||
return templ
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) GetClone() *template.Template {
|
||||
return t.clone
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) LoadEmbedded() {
|
||||
t.EmbedShortcodes()
|
||||
t.EmbedTemplates()
|
||||
}
|
||||
|
||||
// MarkReady marks the template as "ready for execution". No changes allowed
|
||||
// after this is set.
|
||||
func (t *GoHTMLTemplate) MarkReady() {
|
||||
if t.clone == nil {
|
||||
t.clone = template.Must(t.Template.Clone())
|
||||
}
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) checkState() {
|
||||
if t.clone != nil {
|
||||
panic("template is cloned and cannot be modfified")
|
||||
}
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) AddInternalTemplate(prefix, name, tpl string) error {
|
||||
if prefix != "" {
|
||||
return t.AddTemplate("_internal/"+prefix+"/"+name, tpl)
|
||||
}
|
||||
return t.AddTemplate("_internal/"+name, tpl)
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) AddInternalShortcode(name, content string) error {
|
||||
return t.AddInternalTemplate("shortcodes", name, content)
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) AddTemplate(name, tpl string) error {
|
||||
t.checkState()
|
||||
templ, err := t.New(name).Parse(tpl)
|
||||
if err != nil {
|
||||
t.errors = append(t.errors, &templateErr{name: name, err: err})
|
||||
return err
|
||||
}
|
||||
if err := applyTemplateTransformers(templ); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) AddTemplateFileWithMaster(name, overlayFilename, masterFilename string) error {
|
||||
|
||||
// There is currently no known way to associate a cloned template with an existing one.
|
||||
// This funky master/overlay design will hopefully improve in a future version of Go.
|
||||
//
|
||||
// Simplicity is hard.
|
||||
//
|
||||
// Until then we'll have to live with this hackery.
|
||||
//
|
||||
// See https://github.com/golang/go/issues/14285
|
||||
//
|
||||
// So, to do minimum amount of changes to get this to work:
|
||||
//
|
||||
// 1. Lookup or Parse the master
|
||||
// 2. Parse and store the overlay in a separate map
|
||||
|
||||
masterTpl := t.Lookup(masterFilename)
|
||||
|
||||
if masterTpl == nil {
|
||||
b, err := afero.ReadFile(t.Fs.Source, masterFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
masterTpl, err = t.New(masterFilename).Parse(string(b))
|
||||
|
||||
if err != nil {
|
||||
// TODO(bep) Add a method that does this
|
||||
t.errors = append(t.errors, &templateErr{name: name, err: err})
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
b, err := afero.ReadFile(t.Fs.Source, overlayFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
overlayTpl, err := template.Must(masterTpl.Clone()).Parse(string(b))
|
||||
if err != nil {
|
||||
t.errors = append(t.errors, &templateErr{name: name, err: err})
|
||||
} else {
|
||||
// The extra lookup is a workaround, see
|
||||
// * https://github.com/golang/go/issues/16101
|
||||
// * https://github.com/spf13/hugo/issues/2549
|
||||
overlayTpl = overlayTpl.Lookup(overlayTpl.Name())
|
||||
if err := applyTemplateTransformers(overlayTpl); err != nil {
|
||||
return err
|
||||
}
|
||||
t.overlays[name] = overlayTpl
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) AddAceTemplate(name, basePath, innerPath string, baseContent, innerContent []byte) error {
|
||||
t.checkState()
|
||||
var base, inner *ace.File
|
||||
name = name[:len(name)-len(filepath.Ext(innerPath))] + ".html"
|
||||
|
||||
// Fixes issue #1178
|
||||
basePath = strings.Replace(basePath, "\\", "/", -1)
|
||||
innerPath = strings.Replace(innerPath, "\\", "/", -1)
|
||||
|
||||
if basePath != "" {
|
||||
base = ace.NewFile(basePath, baseContent)
|
||||
inner = ace.NewFile(innerPath, innerContent)
|
||||
} else {
|
||||
base = ace.NewFile(innerPath, innerContent)
|
||||
inner = ace.NewFile("", []byte{})
|
||||
}
|
||||
parsed, err := ace.ParseSource(ace.NewSource(base, inner, []*ace.File{}), nil)
|
||||
if err != nil {
|
||||
t.errors = append(t.errors, &templateErr{name: name, err: err})
|
||||
return err
|
||||
}
|
||||
templ, err := ace.CompileResultWithTemplate(t.New(name), parsed, nil)
|
||||
if err != nil {
|
||||
t.errors = append(t.errors, &templateErr{name: name, err: err})
|
||||
return err
|
||||
}
|
||||
return applyTemplateTransformers(templ)
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) AddTemplateFile(name, baseTemplatePath, path string) error {
|
||||
t.checkState()
|
||||
// get the suffix and switch on that
|
||||
ext := filepath.Ext(path)
|
||||
switch ext {
|
||||
case ".amber":
|
||||
templateName := strings.TrimSuffix(name, filepath.Ext(name)) + ".html"
|
||||
b, err := afero.ReadFile(t.Fs.Source, path)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
amberMu.Lock()
|
||||
templ, err := t.CompileAmberWithTemplate(b, path, t.New(templateName))
|
||||
amberMu.Unlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return applyTemplateTransformers(templ)
|
||||
case ".ace":
|
||||
var innerContent, baseContent []byte
|
||||
innerContent, err := afero.ReadFile(t.Fs.Source, path)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if baseTemplatePath != "" {
|
||||
baseContent, err = afero.ReadFile(t.Fs.Source, baseTemplatePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return t.AddAceTemplate(name, baseTemplatePath, path, baseContent, innerContent)
|
||||
default:
|
||||
|
||||
if baseTemplatePath != "" {
|
||||
return t.AddTemplateFileWithMaster(name, path, baseTemplatePath)
|
||||
}
|
||||
|
||||
b, err := afero.ReadFile(t.Fs.Source, path)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.Log.DEBUG.Printf("Add template file from path %s", path)
|
||||
|
||||
return t.AddTemplate(name, string(b))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) GenerateTemplateNameFrom(base, path string) string {
|
||||
name, _ := filepath.Rel(base, path)
|
||||
return filepath.ToSlash(name)
|
||||
}
|
||||
|
||||
func isDotFile(path string) bool {
|
||||
return filepath.Base(path)[0] == '.'
|
||||
}
|
||||
|
||||
func isBackupFile(path string) bool {
|
||||
return path[len(path)-1] == '~'
|
||||
}
|
||||
|
||||
const baseFileBase = "baseof"
|
||||
|
||||
var aceTemplateInnerMarkers = [][]byte{[]byte("= content")}
|
||||
var goTemplateInnerMarkers = [][]byte{[]byte("{{define"), []byte("{{ define")}
|
||||
|
||||
func isBaseTemplate(path string) bool {
|
||||
return strings.Contains(path, baseFileBase)
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) loadTemplates(absPath string, prefix string) {
|
||||
t.Log.DEBUG.Printf("Load templates from path %q prefix %q", absPath, prefix)
|
||||
walker := func(path string, fi os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
t.Log.DEBUG.Println("Template path", path)
|
||||
if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||
link, err := filepath.EvalSymlinks(absPath)
|
||||
if err != nil {
|
||||
t.Log.ERROR.Printf("Cannot read symbolic link '%s', error was: %s", absPath, err)
|
||||
return nil
|
||||
}
|
||||
linkfi, err := t.Fs.Source.Stat(link)
|
||||
if err != nil {
|
||||
t.Log.ERROR.Printf("Cannot stat '%s', error was: %s", link, err)
|
||||
return nil
|
||||
}
|
||||
if !linkfi.Mode().IsRegular() {
|
||||
t.Log.ERROR.Printf("Symbolic links for directories not supported, skipping '%s'", absPath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if !fi.IsDir() {
|
||||
if isDotFile(path) || isBackupFile(path) || isBaseTemplate(path) {
|
||||
return nil
|
||||
}
|
||||
|
||||
tplName := t.GenerateTemplateNameFrom(absPath, path)
|
||||
|
||||
if prefix != "" {
|
||||
tplName = strings.Trim(prefix, "/") + "/" + tplName
|
||||
}
|
||||
|
||||
var baseTemplatePath string
|
||||
|
||||
// Ace and Go templates may have both a base and inner template.
|
||||
pathDir := filepath.Dir(path)
|
||||
if filepath.Ext(path) != ".amber" && !strings.HasSuffix(pathDir, "partials") && !strings.HasSuffix(pathDir, "shortcodes") {
|
||||
|
||||
innerMarkers := goTemplateInnerMarkers
|
||||
baseFileName := fmt.Sprintf("%s.html", baseFileBase)
|
||||
|
||||
if filepath.Ext(path) == ".ace" {
|
||||
innerMarkers = aceTemplateInnerMarkers
|
||||
baseFileName = fmt.Sprintf("%s.ace", baseFileBase)
|
||||
}
|
||||
|
||||
// This may be a view that shouldn't have base template
|
||||
// Have to look inside it to make sure
|
||||
needsBase, err := helpers.FileContainsAny(path, innerMarkers, t.Fs.Source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if needsBase {
|
||||
|
||||
layoutDir := t.PathSpec.GetLayoutDirPath()
|
||||
currBaseFilename := fmt.Sprintf("%s-%s", helpers.Filename(path), baseFileName)
|
||||
templateDir := filepath.Dir(path)
|
||||
themeDir := filepath.Join(t.PathSpec.GetThemeDir())
|
||||
relativeThemeLayoutsDir := filepath.Join(t.PathSpec.GetRelativeThemeDir(), "layouts")
|
||||
|
||||
var baseTemplatedDir string
|
||||
|
||||
if strings.HasPrefix(templateDir, relativeThemeLayoutsDir) {
|
||||
baseTemplatedDir = strings.TrimPrefix(templateDir, relativeThemeLayoutsDir)
|
||||
} else {
|
||||
baseTemplatedDir = strings.TrimPrefix(templateDir, layoutDir)
|
||||
}
|
||||
|
||||
baseTemplatedDir = strings.TrimPrefix(baseTemplatedDir, helpers.FilePathSeparator)
|
||||
|
||||
// Look for base template in the follwing order:
|
||||
// 1. <current-path>/<template-name>-baseof.<suffix>, e.g. list-baseof.<suffix>.
|
||||
// 2. <current-path>/baseof.<suffix>
|
||||
// 3. _default/<template-name>-baseof.<suffix>, e.g. list-baseof.<suffix>.
|
||||
// 4. _default/baseof.<suffix>
|
||||
// For each of the steps above, it will first look in the project, then, if theme is set,
|
||||
// in the theme's layouts folder.
|
||||
|
||||
pairsToCheck := [][]string{
|
||||
[]string{baseTemplatedDir, currBaseFilename},
|
||||
[]string{baseTemplatedDir, baseFileName},
|
||||
[]string{"_default", currBaseFilename},
|
||||
[]string{"_default", baseFileName},
|
||||
}
|
||||
|
||||
Loop:
|
||||
for _, pair := range pairsToCheck {
|
||||
pathsToCheck := basePathsToCheck(pair, layoutDir, themeDir)
|
||||
for _, pathToCheck := range pathsToCheck {
|
||||
if ok, err := helpers.Exists(pathToCheck, t.Fs.Source); err == nil && ok {
|
||||
baseTemplatePath = pathToCheck
|
||||
break Loop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := t.AddTemplateFile(tplName, baseTemplatePath, path); err != nil {
|
||||
t.Log.ERROR.Printf("Failed to add template %s in path %s: %s", tplName, path, err)
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := helpers.SymbolicWalk(t.Fs.Source, absPath, walker); err != nil {
|
||||
t.Log.ERROR.Printf("Failed to load templates: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func basePathsToCheck(path []string, layoutDir, themeDir string) []string {
|
||||
// Always look in the project.
|
||||
pathsToCheck := []string{filepath.Join((append([]string{layoutDir}, path...))...)}
|
||||
|
||||
// May have a theme
|
||||
if themeDir != "" {
|
||||
pathsToCheck = append(pathsToCheck, filepath.Join((append([]string{themeDir, "layouts"}, path...))...))
|
||||
}
|
||||
|
||||
return pathsToCheck
|
||||
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) LoadTemplatesWithPrefix(absPath string, prefix string) {
|
||||
t.loadTemplates(absPath, prefix)
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) LoadTemplates(absPath string) {
|
||||
t.loadTemplates(absPath, "")
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) PrintErrors() {
|
||||
for i, e := range t.errors {
|
||||
t.Log.ERROR.Println(i, ":", e.err)
|
||||
}
|
||||
// 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
|
||||
LoadTemplates(absPath string)
|
||||
LoadTemplatesWithPrefix(absPath, prefix string)
|
||||
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
|
||||
PrintErrors()
|
||||
Funcs(funcMap template.FuncMap)
|
||||
MarkReady()
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tpl
|
||||
package tplimpl
|
||||
|
||||
import (
|
||||
"html/template"
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2016 The Hugo Authors. All rights reserved.
|
||||
// Copyright 2017 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.
|
||||
|
@ -11,7 +11,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tpl
|
||||
package tplimpl
|
||||
|
||||
import (
|
||||
"reflect"
|
575
tpl/tplimpl/template.go
Normal file
575
tpl/tplimpl/template.go
Normal file
|
@ -0,0 +1,575 @@
|
|||
// Copyright 2016 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 tplimpl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"sync"
|
||||
|
||||
"github.com/eknkc/amber"
|
||||
"github.com/spf13/afero"
|
||||
bp "github.com/spf13/hugo/bufferpool"
|
||||
"github.com/spf13/hugo/deps"
|
||||
"github.com/spf13/hugo/helpers"
|
||||
"github.com/yosssi/ace"
|
||||
)
|
||||
|
||||
// TODO(bep) globals get rid of the rest of the jww.ERR etc.
|
||||
|
||||
// Protecting global map access (Amber)
|
||||
var amberMu sync.Mutex
|
||||
|
||||
type templateErr struct {
|
||||
name string
|
||||
err error
|
||||
}
|
||||
|
||||
type GoHTMLTemplate struct {
|
||||
*template.Template
|
||||
|
||||
clone *template.Template
|
||||
|
||||
// a separate storage for the overlays created from cloned master templates.
|
||||
// note: No mutex protection, so we add these in one Go routine, then just read.
|
||||
overlays map[string]*template.Template
|
||||
|
||||
errors []*templateErr
|
||||
|
||||
funcster *templateFuncster
|
||||
|
||||
amberFuncMap template.FuncMap
|
||||
|
||||
*deps.Deps
|
||||
}
|
||||
|
||||
type TemplateProvider struct{}
|
||||
|
||||
var DefaultTemplateProvider *TemplateProvider
|
||||
|
||||
// Update updates the Hugo Template System in the provided Deps.
|
||||
// with all the additional features, templates & functions
|
||||
func (*TemplateProvider) Update(deps *deps.Deps) error {
|
||||
// TODO(bep) check that this isn't called too many times.
|
||||
tmpl := &GoHTMLTemplate{
|
||||
Template: template.New(""),
|
||||
overlays: make(map[string]*template.Template),
|
||||
errors: make([]*templateErr, 0),
|
||||
Deps: deps,
|
||||
}
|
||||
|
||||
deps.Tmpl = tmpl
|
||||
|
||||
tmpl.initFuncs(deps)
|
||||
|
||||
tmpl.LoadEmbedded()
|
||||
|
||||
if deps.WithTemplate != nil {
|
||||
err := deps.WithTemplate(tmpl)
|
||||
if err != nil {
|
||||
tmpl.errors = append(tmpl.errors, &templateErr{"init", err})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
tmpl.MarkReady()
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// Clone clones
|
||||
func (*TemplateProvider) Clone(d *deps.Deps) error {
|
||||
|
||||
t := d.Tmpl.(*GoHTMLTemplate)
|
||||
|
||||
// 1. Clone the clone with new template funcs
|
||||
// 2. Clone any overlays with new template funcs
|
||||
|
||||
tmpl := &GoHTMLTemplate{
|
||||
Template: template.Must(t.Template.Clone()),
|
||||
overlays: make(map[string]*template.Template),
|
||||
errors: make([]*templateErr, 0),
|
||||
Deps: d,
|
||||
}
|
||||
|
||||
d.Tmpl = tmpl
|
||||
tmpl.initFuncs(d)
|
||||
|
||||
for k, v := range t.overlays {
|
||||
vc := template.Must(v.Clone())
|
||||
// The extra lookup is a workaround, see
|
||||
// * https://github.com/golang/go/issues/16101
|
||||
// * https://github.com/spf13/hugo/issues/2549
|
||||
vc = vc.Lookup(vc.Name())
|
||||
vc.Funcs(tmpl.funcster.funcMap)
|
||||
tmpl.overlays[k] = vc
|
||||
}
|
||||
|
||||
tmpl.MarkReady()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) initFuncs(d *deps.Deps) {
|
||||
|
||||
t.funcster = newTemplateFuncster(d)
|
||||
|
||||
// The URL funcs in the funcMap is somewhat language dependent,
|
||||
// so we need to wait until the language and site config is loaded.
|
||||
t.funcster.initFuncMap()
|
||||
|
||||
t.amberFuncMap = template.FuncMap{}
|
||||
|
||||
amberMu.Lock()
|
||||
for k, v := range amber.FuncMap {
|
||||
t.amberFuncMap[k] = v
|
||||
}
|
||||
|
||||
for k, v := range t.funcster.funcMap {
|
||||
t.amberFuncMap[k] = v
|
||||
// Hacky, but we need to make sure that the func names are in the global map.
|
||||
amber.FuncMap[k] = func() string {
|
||||
panic("should never be invoked")
|
||||
}
|
||||
}
|
||||
amberMu.Unlock()
|
||||
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) Funcs(funcMap template.FuncMap) {
|
||||
t.Template.Funcs(funcMap)
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) Partial(name string, contextList ...interface{}) template.HTML {
|
||||
if strings.HasPrefix("partials/", name) {
|
||||
name = name[8:]
|
||||
}
|
||||
var context interface{}
|
||||
|
||||
if len(contextList) == 0 {
|
||||
context = nil
|
||||
} else {
|
||||
context = contextList[0]
|
||||
}
|
||||
return t.ExecuteTemplateToHTML(context, "partials/"+name, "theme/partials/"+name)
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) executeTemplate(context interface{}, w io.Writer, layouts ...string) {
|
||||
var worked bool
|
||||
for _, layout := range layouts {
|
||||
templ := t.Lookup(layout)
|
||||
if templ == nil {
|
||||
layout += ".html"
|
||||
templ = t.Lookup(layout)
|
||||
}
|
||||
|
||||
if templ != nil {
|
||||
if err := templ.Execute(w, context); err != nil {
|
||||
helpers.DistinctErrorLog.Println(layout, err)
|
||||
}
|
||||
worked = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !worked {
|
||||
t.Log.ERROR.Println("Unable to render", layouts)
|
||||
t.Log.ERROR.Println("Expecting to find a template in either the theme/layouts or /layouts in one of the following relative locations", layouts)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) ExecuteTemplateToHTML(context interface{}, layouts ...string) template.HTML {
|
||||
b := bp.GetBuffer()
|
||||
defer bp.PutBuffer(b)
|
||||
t.executeTemplate(context, b, layouts...)
|
||||
return template.HTML(b.String())
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) Lookup(name string) *template.Template {
|
||||
|
||||
if templ := t.Template.Lookup(name); templ != nil {
|
||||
return templ
|
||||
}
|
||||
|
||||
if t.overlays != nil {
|
||||
if templ, ok := t.overlays[name]; ok {
|
||||
return templ
|
||||
}
|
||||
}
|
||||
|
||||
// The clone is used for the non-renderable HTML pages (p.IsRenderable == false) that is parsed
|
||||
// as Go templates late in the build process.
|
||||
if t.clone != nil {
|
||||
if templ := t.clone.Lookup(name); templ != nil {
|
||||
return templ
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) GetClone() *template.Template {
|
||||
return t.clone
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) LoadEmbedded() {
|
||||
t.EmbedShortcodes()
|
||||
t.EmbedTemplates()
|
||||
}
|
||||
|
||||
// MarkReady marks the template as "ready for execution". No changes allowed
|
||||
// after this is set.
|
||||
func (t *GoHTMLTemplate) MarkReady() {
|
||||
if t.clone == nil {
|
||||
t.clone = template.Must(t.Template.Clone())
|
||||
}
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) checkState() {
|
||||
if t.clone != nil {
|
||||
panic("template is cloned and cannot be modfified")
|
||||
}
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) AddInternalTemplate(prefix, name, tpl string) error {
|
||||
if prefix != "" {
|
||||
return t.AddTemplate("_internal/"+prefix+"/"+name, tpl)
|
||||
}
|
||||
return t.AddTemplate("_internal/"+name, tpl)
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) AddInternalShortcode(name, content string) error {
|
||||
return t.AddInternalTemplate("shortcodes", name, content)
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) AddTemplate(name, tpl string) error {
|
||||
t.checkState()
|
||||
templ, err := t.New(name).Parse(tpl)
|
||||
if err != nil {
|
||||
t.errors = append(t.errors, &templateErr{name: name, err: err})
|
||||
return err
|
||||
}
|
||||
if err := applyTemplateTransformers(templ); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) AddTemplateFileWithMaster(name, overlayFilename, masterFilename string) error {
|
||||
|
||||
// There is currently no known way to associate a cloned template with an existing one.
|
||||
// This funky master/overlay design will hopefully improve in a future version of Go.
|
||||
//
|
||||
// Simplicity is hard.
|
||||
//
|
||||
// Until then we'll have to live with this hackery.
|
||||
//
|
||||
// See https://github.com/golang/go/issues/14285
|
||||
//
|
||||
// So, to do minimum amount of changes to get this to work:
|
||||
//
|
||||
// 1. Lookup or Parse the master
|
||||
// 2. Parse and store the overlay in a separate map
|
||||
|
||||
masterTpl := t.Lookup(masterFilename)
|
||||
|
||||
if masterTpl == nil {
|
||||
b, err := afero.ReadFile(t.Fs.Source, masterFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
masterTpl, err = t.New(masterFilename).Parse(string(b))
|
||||
|
||||
if err != nil {
|
||||
// TODO(bep) Add a method that does this
|
||||
t.errors = append(t.errors, &templateErr{name: name, err: err})
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
b, err := afero.ReadFile(t.Fs.Source, overlayFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
overlayTpl, err := template.Must(masterTpl.Clone()).Parse(string(b))
|
||||
if err != nil {
|
||||
t.errors = append(t.errors, &templateErr{name: name, err: err})
|
||||
} else {
|
||||
// The extra lookup is a workaround, see
|
||||
// * https://github.com/golang/go/issues/16101
|
||||
// * https://github.com/spf13/hugo/issues/2549
|
||||
overlayTpl = overlayTpl.Lookup(overlayTpl.Name())
|
||||
if err := applyTemplateTransformers(overlayTpl); err != nil {
|
||||
return err
|
||||
}
|
||||
t.overlays[name] = overlayTpl
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) AddAceTemplate(name, basePath, innerPath string, baseContent, innerContent []byte) error {
|
||||
t.checkState()
|
||||
var base, inner *ace.File
|
||||
name = name[:len(name)-len(filepath.Ext(innerPath))] + ".html"
|
||||
|
||||
// Fixes issue #1178
|
||||
basePath = strings.Replace(basePath, "\\", "/", -1)
|
||||
innerPath = strings.Replace(innerPath, "\\", "/", -1)
|
||||
|
||||
if basePath != "" {
|
||||
base = ace.NewFile(basePath, baseContent)
|
||||
inner = ace.NewFile(innerPath, innerContent)
|
||||
} else {
|
||||
base = ace.NewFile(innerPath, innerContent)
|
||||
inner = ace.NewFile("", []byte{})
|
||||
}
|
||||
parsed, err := ace.ParseSource(ace.NewSource(base, inner, []*ace.File{}), nil)
|
||||
if err != nil {
|
||||
t.errors = append(t.errors, &templateErr{name: name, err: err})
|
||||
return err
|
||||
}
|
||||
templ, err := ace.CompileResultWithTemplate(t.New(name), parsed, nil)
|
||||
if err != nil {
|
||||
t.errors = append(t.errors, &templateErr{name: name, err: err})
|
||||
return err
|
||||
}
|
||||
return applyTemplateTransformers(templ)
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) AddTemplateFile(name, baseTemplatePath, path string) error {
|
||||
t.checkState()
|
||||
// get the suffix and switch on that
|
||||
ext := filepath.Ext(path)
|
||||
switch ext {
|
||||
case ".amber":
|
||||
templateName := strings.TrimSuffix(name, filepath.Ext(name)) + ".html"
|
||||
b, err := afero.ReadFile(t.Fs.Source, path)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
amberMu.Lock()
|
||||
templ, err := t.CompileAmberWithTemplate(b, path, t.New(templateName))
|
||||
amberMu.Unlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return applyTemplateTransformers(templ)
|
||||
case ".ace":
|
||||
var innerContent, baseContent []byte
|
||||
innerContent, err := afero.ReadFile(t.Fs.Source, path)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if baseTemplatePath != "" {
|
||||
baseContent, err = afero.ReadFile(t.Fs.Source, baseTemplatePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return t.AddAceTemplate(name, baseTemplatePath, path, baseContent, innerContent)
|
||||
default:
|
||||
|
||||
if baseTemplatePath != "" {
|
||||
return t.AddTemplateFileWithMaster(name, path, baseTemplatePath)
|
||||
}
|
||||
|
||||
b, err := afero.ReadFile(t.Fs.Source, path)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.Log.DEBUG.Printf("Add template file from path %s", path)
|
||||
|
||||
return t.AddTemplate(name, string(b))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) GenerateTemplateNameFrom(base, path string) string {
|
||||
name, _ := filepath.Rel(base, path)
|
||||
return filepath.ToSlash(name)
|
||||
}
|
||||
|
||||
func isDotFile(path string) bool {
|
||||
return filepath.Base(path)[0] == '.'
|
||||
}
|
||||
|
||||
func isBackupFile(path string) bool {
|
||||
return path[len(path)-1] == '~'
|
||||
}
|
||||
|
||||
const baseFileBase = "baseof"
|
||||
|
||||
var aceTemplateInnerMarkers = [][]byte{[]byte("= content")}
|
||||
var goTemplateInnerMarkers = [][]byte{[]byte("{{define"), []byte("{{ define")}
|
||||
|
||||
func isBaseTemplate(path string) bool {
|
||||
return strings.Contains(path, baseFileBase)
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) loadTemplates(absPath string, prefix string) {
|
||||
t.Log.DEBUG.Printf("Load templates from path %q prefix %q", absPath, prefix)
|
||||
walker := func(path string, fi os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
t.Log.DEBUG.Println("Template path", path)
|
||||
if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||
link, err := filepath.EvalSymlinks(absPath)
|
||||
if err != nil {
|
||||
t.Log.ERROR.Printf("Cannot read symbolic link '%s', error was: %s", absPath, err)
|
||||
return nil
|
||||
}
|
||||
linkfi, err := t.Fs.Source.Stat(link)
|
||||
if err != nil {
|
||||
t.Log.ERROR.Printf("Cannot stat '%s', error was: %s", link, err)
|
||||
return nil
|
||||
}
|
||||
if !linkfi.Mode().IsRegular() {
|
||||
t.Log.ERROR.Printf("Symbolic links for directories not supported, skipping '%s'", absPath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if !fi.IsDir() {
|
||||
if isDotFile(path) || isBackupFile(path) || isBaseTemplate(path) {
|
||||
return nil
|
||||
}
|
||||
|
||||
tplName := t.GenerateTemplateNameFrom(absPath, path)
|
||||
|
||||
if prefix != "" {
|
||||
tplName = strings.Trim(prefix, "/") + "/" + tplName
|
||||
}
|
||||
|
||||
var baseTemplatePath string
|
||||
|
||||
// Ace and Go templates may have both a base and inner template.
|
||||
pathDir := filepath.Dir(path)
|
||||
if filepath.Ext(path) != ".amber" && !strings.HasSuffix(pathDir, "partials") && !strings.HasSuffix(pathDir, "shortcodes") {
|
||||
|
||||
innerMarkers := goTemplateInnerMarkers
|
||||
baseFileName := fmt.Sprintf("%s.html", baseFileBase)
|
||||
|
||||
if filepath.Ext(path) == ".ace" {
|
||||
innerMarkers = aceTemplateInnerMarkers
|
||||
baseFileName = fmt.Sprintf("%s.ace", baseFileBase)
|
||||
}
|
||||
|
||||
// This may be a view that shouldn't have base template
|
||||
// Have to look inside it to make sure
|
||||
needsBase, err := helpers.FileContainsAny(path, innerMarkers, t.Fs.Source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if needsBase {
|
||||
|
||||
layoutDir := t.PathSpec.GetLayoutDirPath()
|
||||
currBaseFilename := fmt.Sprintf("%s-%s", helpers.Filename(path), baseFileName)
|
||||
templateDir := filepath.Dir(path)
|
||||
themeDir := filepath.Join(t.PathSpec.GetThemeDir())
|
||||
relativeThemeLayoutsDir := filepath.Join(t.PathSpec.GetRelativeThemeDir(), "layouts")
|
||||
|
||||
var baseTemplatedDir string
|
||||
|
||||
if strings.HasPrefix(templateDir, relativeThemeLayoutsDir) {
|
||||
baseTemplatedDir = strings.TrimPrefix(templateDir, relativeThemeLayoutsDir)
|
||||
} else {
|
||||
baseTemplatedDir = strings.TrimPrefix(templateDir, layoutDir)
|
||||
}
|
||||
|
||||
baseTemplatedDir = strings.TrimPrefix(baseTemplatedDir, helpers.FilePathSeparator)
|
||||
|
||||
// Look for base template in the follwing order:
|
||||
// 1. <current-path>/<template-name>-baseof.<suffix>, e.g. list-baseof.<suffix>.
|
||||
// 2. <current-path>/baseof.<suffix>
|
||||
// 3. _default/<template-name>-baseof.<suffix>, e.g. list-baseof.<suffix>.
|
||||
// 4. _default/baseof.<suffix>
|
||||
// For each of the steps above, it will first look in the project, then, if theme is set,
|
||||
// in the theme's layouts folder.
|
||||
|
||||
pairsToCheck := [][]string{
|
||||
[]string{baseTemplatedDir, currBaseFilename},
|
||||
[]string{baseTemplatedDir, baseFileName},
|
||||
[]string{"_default", currBaseFilename},
|
||||
[]string{"_default", baseFileName},
|
||||
}
|
||||
|
||||
Loop:
|
||||
for _, pair := range pairsToCheck {
|
||||
pathsToCheck := basePathsToCheck(pair, layoutDir, themeDir)
|
||||
for _, pathToCheck := range pathsToCheck {
|
||||
if ok, err := helpers.Exists(pathToCheck, t.Fs.Source); err == nil && ok {
|
||||
baseTemplatePath = pathToCheck
|
||||
break Loop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := t.AddTemplateFile(tplName, baseTemplatePath, path); err != nil {
|
||||
t.Log.ERROR.Printf("Failed to add template %s in path %s: %s", tplName, path, err)
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := helpers.SymbolicWalk(t.Fs.Source, absPath, walker); err != nil {
|
||||
t.Log.ERROR.Printf("Failed to load templates: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func basePathsToCheck(path []string, layoutDir, themeDir string) []string {
|
||||
// Always look in the project.
|
||||
pathsToCheck := []string{filepath.Join((append([]string{layoutDir}, path...))...)}
|
||||
|
||||
// May have a theme
|
||||
if themeDir != "" {
|
||||
pathsToCheck = append(pathsToCheck, filepath.Join((append([]string{themeDir, "layouts"}, path...))...))
|
||||
}
|
||||
|
||||
return pathsToCheck
|
||||
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) LoadTemplatesWithPrefix(absPath string, prefix string) {
|
||||
t.loadTemplates(absPath, prefix)
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) LoadTemplates(absPath string) {
|
||||
t.loadTemplates(absPath, "")
|
||||
}
|
||||
|
||||
func (t *GoHTMLTemplate) PrintErrors() {
|
||||
for i, e := range t.errors {
|
||||
t.Log.ERROR.Println(i, ":", e.err)
|
||||
}
|
||||
}
|
|
@ -11,7 +11,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tpl
|
||||
package tplimpl
|
||||
|
||||
import (
|
||||
"errors"
|
|
@ -10,7 +10,7 @@
|
|||
// 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
|
||||
package tplimpl
|
||||
|
||||
import (
|
||||
"bytes"
|
|
@ -11,7 +11,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tpl
|
||||
package tplimpl
|
||||
|
||||
type Tmpl struct {
|
||||
Name string
|
|
@ -11,7 +11,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tpl
|
||||
package tplimpl
|
||||
|
||||
import (
|
||||
"errors"
|
|
@ -11,7 +11,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tpl
|
||||
package tplimpl
|
||||
|
||||
import (
|
||||
"html/template"
|
|
@ -13,7 +13,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tpl
|
||||
package tplimpl
|
||||
|
||||
import (
|
||||
"bytes"
|
|
@ -11,7 +11,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tpl
|
||||
package tplimpl
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -31,7 +31,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/hugo/tplapi"
|
||||
"github.com/spf13/hugo/tpl"
|
||||
|
||||
"github.com/spf13/hugo/deps"
|
||||
"github.com/spf13/hugo/helpers"
|
||||
|
@ -273,7 +273,7 @@ urlize: bat-man
|
|||
v.Set("CurrentContentLanguage", helpers.NewLanguage("en", v))
|
||||
|
||||
config := newDepsConfig(v)
|
||||
config.WithTemplate = func(templ tplapi.Template) error {
|
||||
config.WithTemplate = func(templ tpl.Template) error {
|
||||
if _, err := templ.New("test").Parse(in); err != nil {
|
||||
t.Fatal("Got error on parse", err)
|
||||
}
|
||||
|
@ -2862,7 +2862,7 @@ func TestPartialCached(t *testing.T) {
|
|||
|
||||
config := newDepsConfig(viper.New())
|
||||
|
||||
config.WithTemplate = func(templ tplapi.Template) error {
|
||||
config.WithTemplate = func(templ tpl.Template) error {
|
||||
err := templ.AddTemplate("testroot", tmp)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -2901,7 +2901,7 @@ func TestPartialCached(t *testing.T) {
|
|||
|
||||
func BenchmarkPartial(b *testing.B) {
|
||||
config := newDepsConfig(viper.New())
|
||||
config.WithTemplate = func(templ tplapi.Template) error {
|
||||
config.WithTemplate = func(templ tpl.Template) error {
|
||||
err := templ.AddTemplate("testroot", `{{ partial "bench1" . }}`)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -2932,7 +2932,7 @@ func BenchmarkPartial(b *testing.B) {
|
|||
|
||||
func BenchmarkPartialCached(b *testing.B) {
|
||||
config := newDepsConfig(viper.New())
|
||||
config.WithTemplate = func(templ tplapi.Template) error {
|
||||
config.WithTemplate = func(templ tpl.Template) error {
|
||||
err := templ.AddTemplate("testroot", `{{ partialCached "bench1" . }}`)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -2978,7 +2978,7 @@ func newTestFuncsterWithViper(v *viper.Viper) *templateFuncster {
|
|||
|
||||
func newTestTemplate(t *testing.T, name, template string) *template.Template {
|
||||
config := newDepsConfig(viper.New())
|
||||
config.WithTemplate = func(templ tplapi.Template) error {
|
||||
config.WithTemplate = func(templ tpl.Template) error {
|
||||
err := templ.AddTemplate(name, template)
|
||||
if err != nil {
|
||||
return err
|
|
@ -11,7 +11,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tpl
|
||||
package tplimpl
|
||||
|
||||
import (
|
||||
"bytes"
|
|
@ -11,7 +11,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tpl
|
||||
package tplimpl
|
||||
|
||||
import (
|
||||
"bytes"
|
|
@ -11,7 +11,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tpl
|
||||
package tplimpl
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -27,7 +27,7 @@ import (
|
|||
"github.com/spf13/afero"
|
||||
"github.com/spf13/hugo/deps"
|
||||
|
||||
"github.com/spf13/hugo/tplapi"
|
||||
"github.com/spf13/hugo/tpl"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -74,7 +74,7 @@ html lang=en
|
|||
d := "DATA"
|
||||
|
||||
config := newDepsConfig(viper.New())
|
||||
config.WithTemplate = func(templ tplapi.Template) error {
|
||||
config.WithTemplate = func(templ tpl.Template) error {
|
||||
return templ.AddAceTemplate("mytemplate.ace", basePath, innerPath,
|
||||
[]byte(this.baseContent), []byte(this.innerContent))
|
||||
}
|
||||
|
@ -144,7 +144,7 @@ func TestAddTemplateFileWithMaster(t *testing.T) {
|
|||
finalTplName := "tp"
|
||||
|
||||
config := newDepsConfig(viper.New())
|
||||
config.WithTemplate = func(templ tplapi.Template) error {
|
||||
config.WithTemplate = func(templ tpl.Template) error {
|
||||
|
||||
err := templ.AddTemplateFileWithMaster(finalTplName, overlayTplName, masterTplName)
|
||||
|
||||
|
@ -284,7 +284,7 @@ func TestTplGoFuzzReports(t *testing.T) {
|
|||
|
||||
config := newDepsConfig(viper.New())
|
||||
|
||||
config.WithTemplate = func(templ tplapi.Template) error {
|
||||
config.WithTemplate = func(templ tpl.Template) error {
|
||||
return templ.AddTemplate("fuzz", this.data)
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
package tplapi
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"io"
|
||||
)
|
||||
|
||||
// TODO(bep) make smaller
|
||||
// TODO(bep) consider putting this into /tpl and the implementation in /tpl/tplimpl or something
|
||||
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
|
||||
LoadTemplates(absPath string)
|
||||
LoadTemplatesWithPrefix(absPath, prefix string)
|
||||
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
|
||||
PrintErrors()
|
||||
Funcs(funcMap template.FuncMap)
|
||||
MarkReady()
|
||||
}
|
Loading…
Reference in a new issue