summaryrefslogtreecommitdiffstats
path: root/tpl
diff options
context:
space:
mode:
Diffstat (limited to 'tpl')
-rw-r--r--tpl/collections/apply_test.go4
-rw-r--r--tpl/os/init.go4
-rw-r--r--tpl/os/os.go2
-rw-r--r--tpl/partials/partials.go11
-rw-r--r--tpl/resources/init.go68
-rw-r--r--tpl/resources/resources.go255
-rw-r--r--tpl/template.go15
-rw-r--r--tpl/tplimpl/template.go97
-rw-r--r--tpl/tplimpl/templateFuncster.go10
-rw-r--r--tpl/tplimpl/templateProvider.go2
-rw-r--r--tpl/tplimpl/template_funcs.go12
-rw-r--r--tpl/tplimpl/template_funcs_test.go9
-rw-r--r--tpl/tplimpl/template_test.go11
13 files changed, 452 insertions, 48 deletions
diff --git a/tpl/collections/apply_test.go b/tpl/collections/apply_test.go
index de24b06c8..0878844b2 100644
--- a/tpl/collections/apply_test.go
+++ b/tpl/collections/apply_test.go
@@ -25,8 +25,8 @@ import (
type templateFinder int
-func (templateFinder) Lookup(name string) *tpl.TemplateAdapter {
- return nil
+func (templateFinder) Lookup(name string) (tpl.Template, bool) {
+ return nil, false
}
func (templateFinder) GetFuncs() map[string]interface{} {
diff --git a/tpl/os/init.go b/tpl/os/init.go
index 012f43b1f..3ef8702d6 100644
--- a/tpl/os/init.go
+++ b/tpl/os/init.go
@@ -37,14 +37,14 @@ func init() {
ns.AddMethodMapping(ctx.ReadDir,
[]string{"readDir"},
[][2]string{
- {`{{ range (readDir ".") }}{{ .Name }}{{ end }}`, "README.txt"},
+ {`{{ range (readDir "files") }}{{ .Name }}{{ end }}`, "README.txt"},
},
)
ns.AddMethodMapping(ctx.ReadFile,
[]string{"readFile"},
[][2]string{
- {`{{ readFile "README.txt" }}`, `Hugo Rocks!`},
+ {`{{ readFile "files/README.txt" }}`, `Hugo Rocks!`},
},
)
diff --git a/tpl/os/os.go b/tpl/os/os.go
index f7f9537ff..79d035d7e 100644
--- a/tpl/os/os.go
+++ b/tpl/os/os.go
@@ -34,7 +34,7 @@ func New(deps *deps.Deps) *Namespace {
if deps.Fs != nil {
rfs = deps.Fs.WorkingDir
if deps.PathSpec != nil && deps.PathSpec.BaseFs != nil {
- rfs = afero.NewReadOnlyFs(afero.NewCopyOnWriteFs(deps.PathSpec.BaseFs.ContentFs, deps.Fs.WorkingDir))
+ rfs = afero.NewReadOnlyFs(afero.NewCopyOnWriteFs(deps.PathSpec.BaseFs.Content.Fs, deps.Fs.WorkingDir))
}
}
diff --git a/tpl/partials/partials.go b/tpl/partials/partials.go
index beb09f426..18b8d7ed6 100644
--- a/tpl/partials/partials.go
+++ b/tpl/partials/partials.go
@@ -63,12 +63,13 @@ func (ns *Namespace) Include(name string, contextList ...interface{}) (interface
}
for _, n := range []string{"partials/" + name, "theme/partials/" + name} {
- templ := ns.deps.Tmpl.Lookup(n)
- if templ == nil {
+ templ, found := ns.deps.Tmpl.Lookup(n)
+
+ if !found {
// For legacy reasons.
- templ = ns.deps.Tmpl.Lookup(n + ".html")
+ templ, found = ns.deps.Tmpl.Lookup(n + ".html")
}
- if templ != nil {
+ if found {
b := bp.GetBuffer()
defer bp.PutBuffer(b)
@@ -76,7 +77,7 @@ func (ns *Namespace) Include(name string, contextList ...interface{}) (interface
return "", err
}
- if _, ok := templ.Template.(*texttemplate.Template); ok {
+ if _, ok := templ.(*texttemplate.Template); ok {
s := b.String()
if ns.deps.Metrics != nil {
ns.deps.Metrics.TrackValue(n, s)
diff --git a/tpl/resources/init.go b/tpl/resources/init.go
new file mode 100644
index 000000000..3e750f325
--- /dev/null
+++ b/tpl/resources/init.go
@@ -0,0 +1,68 @@
+// Copyright 2018 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 resources
+
+import (
+ "github.com/gohugoio/hugo/deps"
+ "github.com/gohugoio/hugo/tpl/internal"
+)
+
+const name = "resources"
+
+func init() {
+ f := func(d *deps.Deps) *internal.TemplateFuncsNamespace {
+ ctx, err := New(d)
+ if err != nil {
+ // TODO(bep) no panic.
+ panic(err)
+ }
+
+ ns := &internal.TemplateFuncsNamespace{
+ Name: name,
+ Context: func(args ...interface{}) interface{} { return ctx },
+ }
+
+ ns.AddMethodMapping(ctx.Get,
+ nil,
+ [][2]string{},
+ )
+
+ // Add aliases for the most common transformations.
+
+ ns.AddMethodMapping(ctx.Fingerprint,
+ []string{"fingerprint"},
+ [][2]string{},
+ )
+
+ ns.AddMethodMapping(ctx.Minify,
+ []string{"minify"},
+ [][2]string{},
+ )
+
+ ns.AddMethodMapping(ctx.ToCSS,
+ []string{"toCSS"},
+ [][2]string{},
+ )
+
+ ns.AddMethodMapping(ctx.PostCSS,
+ []string{"postCSS"},
+ [][2]string{},
+ )
+
+ return ns
+
+ }
+
+ internal.AddTemplateFuncsNamespace(f)
+}
diff --git a/tpl/resources/resources.go b/tpl/resources/resources.go
new file mode 100644
index 000000000..5d4f6e315
--- /dev/null
+++ b/tpl/resources/resources.go
@@ -0,0 +1,255 @@
+// Copyright 2018 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 resources
+
+import (
+ "errors"
+ "fmt"
+ "path/filepath"
+
+ "github.com/gohugoio/hugo/deps"
+ "github.com/gohugoio/hugo/resource"
+ "github.com/gohugoio/hugo/resource/bundler"
+ "github.com/gohugoio/hugo/resource/create"
+ "github.com/gohugoio/hugo/resource/integrity"
+ "github.com/gohugoio/hugo/resource/minifiers"
+ "github.com/gohugoio/hugo/resource/postcss"
+ "github.com/gohugoio/hugo/resource/templates"
+ "github.com/gohugoio/hugo/resource/tocss/scss"
+ "github.com/spf13/cast"
+)
+
+// New returns a new instance of the resources-namespaced template functions.
+func New(deps *deps.Deps) (*Namespace, error) {
+ scssClient, err := scss.New(deps.BaseFs.Assets, deps.ResourceSpec)
+ if err != nil {
+ return nil, err
+ }
+ return &Namespace{
+ deps: deps,
+ scssClient: scssClient,
+ createClient: create.New(deps.ResourceSpec),
+ bundlerClient: bundler.New(deps.ResourceSpec),
+ integrityClient: integrity.New(deps.ResourceSpec),
+ minifyClient: minifiers.New(deps.ResourceSpec),
+ postcssClient: postcss.New(deps.ResourceSpec),
+ templatesClient: templates.New(deps.ResourceSpec, deps.TextTmpl),
+ }, nil
+}
+
+// Namespace provides template functions for the "resources" namespace.
+type Namespace struct {
+ deps *deps.Deps
+
+ createClient *create.Client
+ bundlerClient *bundler.Client
+ scssClient *scss.Client
+ integrityClient *integrity.Client
+ minifyClient *minifiers.Client
+ postcssClient *postcss.Client
+ templatesClient *templates.Client
+}
+
+// Get locates the filename given in Hugo's filesystems: static, assets and content (in that order)
+// and creates a Resource object that can be used for further transformations.
+func (ns *Namespace) Get(filename interface{}) (resource.Resource, error) {
+ filenamestr, err := cast.ToStringE(filename)
+ if err != nil {
+ return nil, err
+ }
+
+ filenamestr = filepath.Clean(filenamestr)
+
+ // Resource Get'ing is currently limited to /assets to make it simpler
+ // to control the behaviour of publishing and partial rebuilding.
+ return ns.createClient.Get(ns.deps.BaseFs.Assets.Fs, filenamestr)
+
+}
+
+// Concat concatenates a slice of Resource objects. These resources must
+// (currently) be of the same Media Type.
+func (ns *Namespace) Concat(targetPathIn interface{}, r []interface{}) (resource.Resource, error) {
+ targetPath, err := cast.ToStringE(targetPathIn)
+ if err != nil {
+ return nil, err
+ }
+ rr := make([]resource.Resource, len(r))
+ for i := 0; i < len(r); i++ {
+ rv, ok := r[i].(resource.Resource)
+ if !ok {
+ return nil, fmt.Errorf("cannot concat type %T", rv)
+ }
+ rr[i] = rv
+ }
+ return ns.bundlerClient.Concat(targetPath, rr)
+}
+
+// FromString creates a Resource from a string published to the relative target path.
+func (ns *Namespace) FromString(targetPathIn, contentIn interface{}) (resource.Resource, error) {
+ targetPath, err := cast.ToStringE(targetPathIn)
+ if err != nil {
+ return nil, err
+ }
+ content, err := cast.ToStringE(contentIn)
+ if err != nil {
+ return nil, err
+ }
+
+ return ns.createClient.FromString(targetPath, content)
+}
+
+// ExecuteAsTemplate creates a Resource from a Go template, parsed and executed with
+// the given data, and published to the relative target path.
+func (ns *Namespace) ExecuteAsTemplate(args ...interface{}) (resource.Resource, error) {
+ if len(args) != 3 {
+ return nil, fmt.Errorf("must provide targetPath, the template data context and a Resource object")
+ }
+ targetPath, err := cast.ToStringE(args[0])
+ if err != nil {
+ return nil, err
+ }
+ data := args[1]
+
+ r, ok := args[2].(resource.Resource)
+ if !ok {
+ return nil, fmt.Errorf("type %T not supported in Resource transformations", args[2])
+ }
+
+ return ns.templatesClient.ExecuteAsTemplate(r, targetPath, data)
+}
+
+// Fingerprint transforms the given Resource with a MD5 hash of the content in
+// the RelPermalink and Permalink.
+func (ns *Namespace) Fingerprint(args ...interface{}) (resource.Resource, error) {
+ if len(args) < 1 || len(args) > 2 {
+ return nil, errors.New("must provide a Resource and (optional) crypto algo")
+ }
+
+ var algo string
+ resIdx := 0
+
+ if len(args) == 2 {
+ resIdx = 1
+ var err error
+ algo, err = cast.ToStringE(args[0])
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ r, ok := args[resIdx].(resource.Resource)
+ if !ok {
+ return nil, fmt.Errorf("%T is not a Resource", args[resIdx])
+ }
+
+ return ns.integrityClient.Fingerprint(r, algo)
+}
+
+// Minify minifies the given Resource using the MediaType to pick the correct
+// minifier.
+func (ns *Namespace) Minify(r resource.Resource) (resource.Resource, error) {
+ return ns.minifyClient.Minify(r)
+}
+
+// ToCSS converts the given Resource to CSS. You can optional provide an Options
+// object or a target path (string) as first argument.
+func (ns *Namespace) ToCSS(args ...interface{}) (resource.Resource, error) {
+ var (
+ r resource.Resource
+ m map[string]interface{}
+ targetPath string
+ err error
+ ok bool
+ )
+
+ r, targetPath, ok = ns.resolveIfFirstArgIsString(args)
+
+ if !ok {
+ r, m, err = ns.resolveArgs(args)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ var options scss.Options
+ if targetPath != "" {
+ options.TargetPath = targetPath
+ } else if m != nil {
+ options, err = scss.DecodeOptions(m)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return ns.scssClient.ToCSS(r, options)
+}
+
+// PostCSS processes the given Resource with PostCSS
+func (ns *Namespace) PostCSS(args ...interface{}) (resource.Resource, error) {
+ r, m, err := ns.resolveArgs(args)
+ if err != nil {
+ return nil, err
+ }
+ var options postcss.Options
+ if m != nil {
+ options, err = postcss.DecodeOptions(m)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return ns.postcssClient.Process(r, options)
+}
+
+// We allow string or a map as the first argument in some cases.
+func (ns *Namespace) resolveIfFirstArgIsString(args []interface{}) (resource.Resource, string, bool) {
+ if len(args) != 2 {
+ return nil, "", false
+ }
+
+ v1, ok1 := args[0].(string)
+ if !ok1 {
+ return nil, "", false
+ }
+ v2, ok2 := args[1].(resource.Resource)
+
+ return v2, v1, ok2
+}
+
+// This roundabout way of doing it is needed to get both pipeline behaviour and options as arguments.
+func (ns *Namespace) resolveArgs(args []interface{}) (resource.Resource, map[string]interface{}, error) {
+ if len(args) == 0 {
+ return nil, nil, errors.New("no Resource provided in transformation")
+ }
+
+ if len(args) == 1 {
+ r, ok := args[0].(resource.Resource)
+ if !ok {
+ return nil, nil, fmt.Errorf("type %T not supported in Resource transformations", args[0])
+ }
+ return r, nil, nil
+ }
+
+ r, ok := args[1].(resource.Resource)
+ if !ok {
+ return nil, nil, fmt.Errorf("type %T not supported in Resource transformations", args[0])
+ }
+
+ m, err := cast.ToStringMapE(args[0])
+ if err != nil {
+ return nil, nil, fmt.Errorf("invalid options type: %s", err)
+ }
+
+ return r, m, nil
+}
diff --git a/tpl/template.go b/tpl/template.go
index e04d2cc6c..2cef92bb2 100644
--- a/tpl/template.go
+++ b/tpl/template.go
@@ -38,13 +38,15 @@ type TemplateHandler interface {
LoadTemplates(prefix string)
PrintErrors()
+ NewTextTemplate() TemplateParseFinder
+
MarkReady()
RebuildClone()
}
// TemplateFinder finds templates.
type TemplateFinder interface {
- Lookup(name string) *TemplateAdapter
+ Lookup(name string) (Template, bool)
}
// Template is the common interface between text/template and html/template.
@@ -53,6 +55,17 @@ type Template interface {
Name() string
}
+// TemplateParser is used to parse ad-hoc templates, e.g. in the Resource chain.
+type TemplateParser interface {
+ Parse(name, tpl string) (Template, error)
+}
+
+// TemplateParseFinder provides both parsing and finding.
+type TemplateParseFinder interface {
+ TemplateParser
+ TemplateFinder
+}
+
// TemplateExecutor adds some extras to Template.
type TemplateExecutor interface {
Template
diff --git a/tpl/tplimpl/template.go b/tpl/tplimpl/template.go
index e838ebc57..f19c312ec 100644
--- a/tpl/tplimpl/template.go
+++ b/tpl/tplimpl/template.go
@@ -55,7 +55,7 @@ var (
_ templateFuncsterTemplater = (*textTemplates)(nil)
)
-// Protecting global map access (Amber)
+// Protecting global map access (Amber)
var amberMu sync.Mutex
type templateErr struct {
@@ -70,18 +70,26 @@ type templateLoader interface {
}
type templateFuncsterTemplater interface {
+ templateFuncsterSetter
tpl.TemplateFinder
setFuncs(funcMap map[string]interface{})
+}
+
+type templateFuncsterSetter interface {
setTemplateFuncster(f *templateFuncster)
}
// templateHandler holds the templates in play.
// It implements the templateLoader and tpl.TemplateHandler interfaces.
type templateHandler struct {
+ mu sync.Mutex
+
// text holds all the pure text templates.
text *textTemplates
html *htmlTemplates
+ extTextTemplates []*textTemplate
+
amberFuncMap template.FuncMap
errors []*templateErr
@@ -93,6 +101,19 @@ type templateHandler struct {
*deps.Deps
}
+// NewTextTemplate provides a text template parser that has all the Hugo
+// template funcs etc. built-in.
+func (t *templateHandler) NewTextTemplate() tpl.TemplateParseFinder {
+ t.mu.Lock()
+ t.mu.Unlock()
+
+ tt := &textTemplate{t: texttemplate.New("")}
+ t.extTextTemplates = append(t.extTextTemplates, tt)
+
+ return tt
+
+}
+
func (t *templateHandler) addError(name string, err error) {
t.errors = append(t.errors, &templateErr{name, err})
}
@@ -111,7 +132,7 @@ func (t *templateHandler) PrintErrors() {
// Lookup tries to find a template with the given name in both template
// collections: First HTML, then the plain text template collection.
-func (t *templateHandler) Lookup(name string) *tpl.TemplateAdapter {
+func (t *templateHandler) Lookup(name string) (tpl.Template, bool) {
if strings.HasPrefix(name, textTmplNamePrefix) {
// The caller has explicitly asked for a text template, so only look
@@ -123,8 +144,8 @@ func (t *templateHandler) Lookup(name string) *tpl.TemplateAdapter {
}
// Look in both
- if te := t.html.Lookup(name); te != nil {
- return te
+ if te, found := t.html.Lookup(name); found {
+ return te, true
}
return t.text.Lookup(name)
@@ -136,7 +157,7 @@ func (t *templateHandler) clone(d *deps.Deps) *templateHandler {
Deps: d,
layoutsFs: d.BaseFs.Layouts.Fs,
html: &htmlTemplates{t: template.Must(t.html.t.Clone()), overlays: make(map[string]*template.Template)},
- text: &textTemplates{t: texttemplate.Must(t.text.t.Clone()), overlays: make(map[string]*texttemplate.Template)},
+ text: &textTemplates{textTemplate: &textTemplate{t: texttemplate.Must(t.text.t.Clone())}, overlays: make(map[string]*texttemplate.Template)},
errors: make([]*templateErr, 0),
}
@@ -171,8 +192,8 @@ func newTemplateAdapter(deps *deps.Deps) *templateHandler {
overlays: make(map[string]*template.Template),
}
textT := &textTemplates{
- t: texttemplate.New(""),
- overlays: make(map[string]*texttemplate.Template),
+ textTemplate: &textTemplate{t: texttemplate.New("")},
+ overlays: make(map[string]*texttemplate.Template),
}
return &templateHandler{
Deps: deps,
@@ -205,12 +226,12 @@ func (t *htmlTemplates) setTemplateFuncster(f *templateFuncster) {
t.funcster = f
}
-func (t *htmlTemplates) Lookup(name string) *tpl.TemplateAdapter {
+func (t *htmlTemplates) Lookup(name string) (tpl.Template, bool) {
templ := t.lookup(name)
if templ == nil {
- return nil
+ return nil, false
}
- return &tpl.TemplateAdapter{Template: templ, Metrics: t.funcster.Deps.Metrics}
+ return &tpl.TemplateAdapter{Template: templ, Metrics: t.funcster.Deps.Metrics}, true
}
func (t *htmlTemplates) lookup(name string) *template.Template {
@@ -233,27 +254,25 @@ func (t *htmlTemplates) lookup(name string) *template.Template {
return nil
}
-type textTemplates struct {
- funcster *templateFuncster
-
- t *texttemplate.Template
+func (t *textTemplates) setTemplateFuncster(f *templateFuncster) {
+ t.funcster = f
+}
+type textTemplates struct {
+ *textTemplate
+ funcster *templateFuncster
clone *texttemplate.Template
cloneClone *texttemplate.Template
overlays map[string]*texttemplate.Template
}
-func (t *textTemplates) setTemplateFuncster(f *templateFuncster) {
- t.funcster = f
-}
-
-func (t *textTemplates) Lookup(name string) *tpl.TemplateAdapter {
+func (t *textTemplates) Lookup(name string) (tpl.Template, bool) {
templ := t.lookup(name)
if templ == nil {
- return nil
+ return nil, false
}
- return &tpl.TemplateAdapter{Template: templ, Metrics: t.funcster.Deps.Metrics}
+ return &tpl.TemplateAdapter{Template: templ, Metrics: t.funcster.Deps.Metrics}, true
}
func (t *textTemplates) lookup(name string) *texttemplate.Template {
@@ -336,9 +355,34 @@ func (t *htmlTemplates) addLateTemplate(name, tpl string) error {
return t.addTemplateIn(t.clone, name, tpl)
}
+type textTemplate struct {
+ t *texttemplate.Template
+}
+
+func (t *textTemplate) Parse(name, tpl string) (tpl.Template, error) {
+ return t.parSeIn(t.t, name, tpl)
+}
+
+func (t *textTemplate) Lookup(name string) (tpl.Template, bool) {
+ tpl := t.t.Lookup(name)
+ return tpl, tpl != nil
+}
+
+func (t *textTemplate) parSeIn(tt *texttemplate.Template, name, tpl string) (*texttemplate.Template, error) {
+ templ, err := tt.New(name).Parse(tpl)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := applyTemplateTransformersToTextTemplate(templ); err != nil {
+ return nil, err
+ }
+ return templ, nil
+}
+
func (t *textTemplates) addTemplateIn(tt *texttemplate.Template, name, tpl string) error {
name = strings.TrimPrefix(name, textTmplNamePrefix)
- templ, err := tt.New(name).Parse(tpl)
+ templ, err := t.parSeIn(tt, name, tpl)
if err != nil {
return err
}
@@ -467,17 +511,22 @@ func (t *templateHandler) initFuncs() {
// Both template types will get their own funcster instance, which
// in the current case contains the same set of funcs.
- for _, funcsterHolder := range []templateFuncsterTemplater{t.html, t.text} {
+ funcMap := createFuncMap(t.Deps)
+ for _, funcsterHolder := range []templateFuncsterSetter{t.html, t.text} {
funcster := newTemplateFuncster(t.Deps)
// The URL funcs in the funcMap is somewhat language dependent,
// so we need to wait until the language and site config is loaded.
- funcster.initFuncMap()
+ funcster.initFuncMap(funcMap)
funcsterHolder.setTemplateFuncster(funcster)
}
+ for _, extText := range t.extTextTemplates {
+ extText.t.Funcs(funcMap)
+ }
+
// Amber is HTML only.
t.amberFuncMap = template.FuncMap{}
diff --git a/tpl/tplimpl/templateFuncster.go b/tpl/tplimpl/templateFuncster.go
index e6bbde8ec..9490123ab 100644
--- a/tpl/tplimpl/templateFuncster.go
+++ b/tpl/tplimpl/templateFuncster.go
@@ -51,12 +51,12 @@ func (t *templateFuncster) partial(name string, contextList ...interface{}) (int
}
for _, n := range []string{"partials/" + name, "theme/partials/" + name} {
- templ := t.Tmpl.Lookup(n)
- if templ == nil {
+ templ, found := t.Tmpl.Lookup(n)
+ if !found {
// For legacy reasons.
- templ = t.Tmpl.Lookup(n + ".html")
+ templ, found = t.Tmpl.Lookup(n + ".html")
}
- if templ != nil {
+ if found {
b := bp.GetBuffer()
defer bp.PutBuffer(b)
@@ -64,7 +64,7 @@ func (t *templateFuncster) partial(name string, contextList ...interface{}) (int
return "", err
}
- if _, ok := templ.Template.(*texttemplate.Template); ok {
+ if _, ok := templ.(*texttemplate.Template); ok {
return b.String(), nil
}
diff --git a/tpl/tplimpl/templateProvider.go b/tpl/tplimpl/templateProvider.go
index af89fed11..df44e81a6 100644
--- a/tpl/tplimpl/templateProvider.go
+++ b/tpl/tplimpl/templateProvider.go
@@ -30,6 +30,8 @@ func (*TemplateProvider) Update(deps *deps.Deps) error {
newTmpl := newTemplateAdapter(deps)
deps.Tmpl = newTmpl
+ deps.TextTmpl = newTmpl.NewTextTemplate()
+
newTmpl.initFuncs()
newTmpl.loadEmbedded()
diff --git a/tpl/tplimpl/template_funcs.go b/tpl/tplimpl/template_funcs.go
index 6ce387aca..f1ed7f36f 100644
--- a/tpl/tplimpl/template_funcs.go
+++ b/tpl/tplimpl/template_funcs.go
@@ -18,6 +18,8 @@ package tplimpl
import (
"html/template"
+ "github.com/gohugoio/hugo/deps"
+
"github.com/gohugoio/hugo/tpl/internal"
// Init the namespaces
@@ -35,6 +37,7 @@ import (
_ "github.com/gohugoio/hugo/tpl/os"
_ "github.com/gohugoio/hugo/tpl/partials"
_ "github.com/gohugoio/hugo/tpl/path"
+ _ "github.com/gohugoio/hugo/tpl/resources"
_ "github.com/gohugoio/hugo/tpl/safe"
_ "github.com/gohugoio/hugo/tpl/strings"
_ "github.com/gohugoio/hugo/tpl/time"
@@ -42,12 +45,12 @@ import (
_ "github.com/gohugoio/hugo/tpl/urls"
)
-func (t *templateFuncster) initFuncMap() {
+func createFuncMap(d *deps.Deps) map[string]interface{} {
funcMap := template.FuncMap{}
// Merge the namespace funcs
for _, nsf := range internal.TemplateFuncsNamespaceRegistry {
- ns := nsf(t.Deps)
+ ns := nsf(d)
if _, exists := funcMap[ns.Name]; exists {
panic(ns.Name + " is a duplicate template func")
}
@@ -61,8 +64,13 @@ func (t *templateFuncster) initFuncMap() {
}
}
+
}
+ return funcMap
+
+}
+func (t *templateFuncster) initFuncMap(funcMap template.FuncMap) {
t.funcMap = funcMap
t.Tmpl.(*templateHandler).setFuncs(funcMap)
}
diff --git a/tpl/tplimpl/template_funcs_test.go b/tpl/tplimpl/template_funcs_test.go
index a1745282d..341be805a 100644
--- a/tpl/tplimpl/template_funcs_test.go
+++ b/tpl/tplimpl/template_funcs_test.go
@@ -51,6 +51,9 @@ func newTestConfig() config.Provider {
v.Set("i18nDir", "i18n")
v.Set("layoutDir", "layouts")
v.Set("archetypeDir", "archetypes")
+ v.Set("assetDir", "assets")
+ v.Set("resourceDir", "resources")
+ v.Set("publishDir", "public")
return v
}
@@ -76,12 +79,13 @@ func TestTemplateFuncsExamples(t *testing.T) {
v.Set("workingDir", workingDir)
v.Set("multilingual", true)
v.Set("contentDir", "content")
+ v.Set("assetDir", "assets")
v.Set("baseURL", "http://mysite.com/hugo/")
v.Set("CurrentContentLanguage", langs.NewLanguage("en", v))
fs := hugofs.NewMem(v)
- afero.WriteFile(fs.Source, filepath.Join(workingDir, "README.txt"), []byte("Hugo Rocks!"), 0755)
+ afero.WriteFile(fs.Source, filepath.Join(workingDir, "files", "README.txt"), []byte("Hugo Rocks!"), 0755)
depsCfg := newDepsConfig(v)
depsCfg.Fs = fs
@@ -113,7 +117,8 @@ func TestTemplateFuncsExamples(t *testing.T) {
require.NoError(t, d.LoadResources())
var b bytes.Buffer
- require.NoError(t, d.Tmpl.Lookup("test").Execute(&b, &data))
+ templ, _ := d.Tmpl.Lookup("test")
+ require.NoError(t, templ.Execute(&b, &data))
if b.String() != expected {
t.Fatalf("%s[%d]: got %q expected %q", ns.Name, i, b.String(), expected)
}