shithub: hugo

Download patch

ref: 7eb71ee06419f9ceedfd701ab2a27513ef448829
parent: c97dae40d9cd24c467f5b8cfbe2ac06f3cdef1d2
author: Bjørn Erik Pedersen <[email protected]>
date: Sun Apr 2 10:20:34 EDT 2017

Revert "tpl: Rework to handle both text and HTML templates"

Will have to take another stab at this ...

This reverts commit 5c5efa03d2512749950b0d05a7d4bde35ecbdc37.

Closes #3260

--- a/deps/deps.go
+++ b/deps/deps.go
@@ -20,7 +20,7 @@
 	Log *jww.Notepad `json:"-"`
 
 	// The templates to use.
-	Tmpl tpl.TemplateHandler `json:"-"`
+	Tmpl tpl.Template `json:"-"`
 
 	// The file systems to use.
 	Fs *hugofs.Fs `json:"-"`
@@ -40,7 +40,7 @@
 	Language *helpers.Language
 
 	templateProvider ResourceProvider
-	WithTemplate     func(templ tpl.TemplateHandler) error `json:"-"`
+	WithTemplate     func(templ tpl.Template) error `json:"-"`
 
 	translationProvider ResourceProvider
 }
@@ -158,7 +158,7 @@
 
 	// Template handling.
 	TemplateProvider ResourceProvider
-	WithTemplate     func(templ tpl.TemplateHandler) error
+	WithTemplate     func(templ tpl.Template) error
 
 	// i18n handling.
 	TranslationProvider ResourceProvider
--- a/hugolib/alias.go
+++ b/hugolib/alias.go
@@ -22,8 +22,6 @@
 	"runtime"
 	"strings"
 
-	"github.com/spf13/hugo/tpl"
-
 	jww "github.com/spf13/jwalterweatherman"
 
 	"github.com/spf13/hugo/helpers"
@@ -37,7 +35,6 @@
 var defaultAliasTemplates *template.Template
 
 func init() {
-	//TODO(bep) consolidate
 	defaultAliasTemplates = template.New("")
 	template.Must(defaultAliasTemplates.New("alias").Parse(alias))
 	template.Must(defaultAliasTemplates.New("alias-xhtml").Parse(aliasXHtml))
@@ -44,12 +41,12 @@
 }
 
 type aliasHandler struct {
-	t         tpl.TemplateHandler
+	Templates *template.Template
 	log       *jww.Notepad
 	allowRoot bool
 }
 
-func newAliasHandler(t tpl.TemplateHandler, l *jww.Notepad, allowRoot bool) aliasHandler {
+func newAliasHandler(t *template.Template, l *jww.Notepad, allowRoot bool) aliasHandler {
 	return aliasHandler{t, l, allowRoot}
 }
 
@@ -59,19 +56,12 @@
 		t = "alias-xhtml"
 	}
 
-	var templ *tpl.TemplateAdapter
-
-	if a.t != nil {
-		templ = a.t.Lookup("alias.html")
+	template := defaultAliasTemplates
+	if a.Templates != nil {
+		template = a.Templates
+		t = "alias.html"
 	}
 
-	if templ == nil {
-		def := defaultAliasTemplates.Lookup(t)
-		if def != nil {
-			templ = &tpl.TemplateAdapter{def}
-		}
-
-	}
 	data := struct {
 		Permalink string
 		Page      *Page
@@ -81,7 +71,7 @@
 	}
 
 	buffer := new(bytes.Buffer)
-	err := templ.Execute(buffer, data)
+	err := template.ExecuteTemplate(buffer, t, data)
 	if err != nil {
 		return nil, err
 	}
@@ -93,7 +83,8 @@
 }
 
 func (s *Site) publishDestAlias(allowRoot bool, path, permalink string, p *Page) (err error) {
-	handler := newAliasHandler(s.Tmpl, s.Log, allowRoot)
+
+	handler := newAliasHandler(s.Tmpl.Lookup("alias.html"), s.Log, allowRoot)
 
 	isXHTML := strings.HasSuffix(path, ".xhtml")
 
--- a/hugolib/embedded_shortcodes_test.go
+++ b/hugolib/embedded_shortcodes_test.go
@@ -335,8 +335,8 @@
 			th      = testHelper{cfg, fs, t}
 		)
 
-		withTemplate := func(templ tpl.TemplateHandler) error {
-			templ.(tpl.TemplateTestMocker).SetFuncs(tweetFuncMap)
+		withTemplate := func(templ tpl.Template) error {
+			templ.Funcs(tweetFuncMap)
 			return nil
 		}
 
@@ -390,8 +390,8 @@
 om:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;\"\u003e\u003ca href=\"https://www.instagram.com/p/BMokmydjG-M/\" style=\" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;\" target=\"_blank\"\u003eA photo posted by Instagram (@instagram)\u003c/a\u003e on \u003ctime style=\" font-family:Arial,sans-serif; font-size:14px; line-height:17px;\" datetime=\"2016-11-10T15:02:28+00:00\"\u003eNov 10, 2016 at 7:02am PST\u003c/time\u003e\u003c/p\u003e\u003c/div\u003e\u003c/blockquote\u003e\n\u003cscript async defer src=\"//platform.instagram.com/en_US/embeds.js\"\u003e\u003c/script\u003e", "width": 658, "version": "1.0", "author_url": "https://www.instagram.com/instagram", "author_id": 25025320, "type": "rich"}`,
 			`(?s)<blockquote class="instagram-media" data-instgrm-version="7" style=" background:#FFF; border:0; .*<script async defer src="//platform.instagram.com/en_US/embeds.js"></script>`,
 		},
-dding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;\"\u003e\u003ca href=\"https://www.instagram.com/p/BMokmydjG-M/\" style=\" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;\" target=\"_blank\"\u003eA photo posted by Instagram (@instagram)\u003c/a\u003e on \u003ctime style=\" font-family:Arial,sans-serif; font-size:14px; line-height:17px;\" datetime=\"2016-11-10T15:02:28+00:00\"\u003eNov 10, 2016 at 7:02am PST\u003c/time\u003e\u003c/p\u003e\u003c/div\u003e\u003c/blockquote\u003e\n\u003cscript async defer src=\"//platform.instagram.com/en_US/embeds.js\"\u003e\u003c/script\u003e", "width": 658, "version": "1.0", "author_url": "https://www.instagram.com/instagram", "author_id": 25025320, "type": "rich"}`,
-			`(?s)<blockquote class="instagram-media" data-instgrm-version="7" style=" background:#FFF; border:0; .*<script async defer src="//platform.instagram.com/en_US/embeds.js"></script>`,
+dding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;\"\u003e\u003ca href=\"https://www.instagram.com/p/BMokmydjG-M/\" style=\" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;\" target=\"_blank\"\u003eA photo posted by Instagram (@instagram)\u003c/a\u003e on \u003ctime style=\" font-family:Arial,sans-serif; font-size:14px; line-height:17px;\" datetime=\"2016-11-10T15:02:28+00:00\"\u003eNov 10, 2016 at 7:02am PST\u003c/time\u003e\u003c/p\u003e\u003c/div\u003e\u003c/blockquote\u003e\n\u003cscript async defer src=\"//platform.instagram.com/en_US/embeds.js\"\u003e\u003c/script\u003e", "width": 658, "version": "1.0", "author_url": "https://www.instagram.com/instagram", "author_id": 25025320, "type": "rich"}`,
+			`(?s)<blockquote class="instagram-media" data-instgrm-version="7" style=" background:#FFF; border:0; .*<script async defer src="//platform.instagram.com/en_US/embeds.js"></script>`,
 gram.com/p/BMokmydjG-M/\" style=\" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;\" target=\"_blank\"\u003eA photo posted by Instagram (@instagram)\u003c/a\u003e on \u003ctime style=\" font-family:Arial,sans-serif; font-size:14px; line-height:17px;\" datetime=\"2016-11-10T15:02:28+00:00\"\u003eNov 10, 2016 at 7:02am PST\u003c/time\u003e\u003c/p\u003e\u003c/div\u003e\u003c/blockquote\u003e\n\u003cscript async defer src=\"//platform.instagram.com/en_US/embeds.js\"\u003e\u003c/script\u003e", "width": 658, "version": "1.0", "author_url": "https://www.instagram.com/instagram", "author_id": 25025320, "type": "rich"}`,
 			`(?s)<blockquote class="instagram-media" data-instgrm-version="7" style=" background:#FFF; border:0; .*<script async defer src="//platform.instagram.com/en_US/embeds.js"></script>`,
 		},
--- a/hugolib/hugo_sites.go
+++ b/hugolib/hugo_sites.go
@@ -127,11 +127,11 @@
 	return newHugoSites(cfg, sites...)
 }
 
-func (s *Site) withSiteTemplates(withTemplates ...func(templ tpl.TemplateHandler) error) func(templ tpl.TemplateHandler) error {
-	return func(templ tpl.TemplateHandler) error {
-		templ.LoadTemplates(s.PathSpec.GetLayoutDirPath(), "")
+func (s *Site) withSiteTemplates(withTemplates ...func(templ tpl.Template) error) func(templ tpl.Template) error {
+	return func(templ tpl.Template) error {
+		templ.LoadTemplates(s.PathSpec.GetLayoutDirPath())
 		if s.PathSpec.ThemeSet() {
-			templ.LoadTemplates(s.PathSpec.GetThemeDir()+"/layouts", "theme")
+			templ.LoadTemplatesWithPrefix(s.PathSpec.GetThemeDir()+"/layouts", "theme")
 		}
 
 		for _, wt := range withTemplates {
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -1384,7 +1384,7 @@
 	if p.Kind == KindPage {
 		if !p.IsRenderable() {
 			self := "__" + p.UniqueID()
-			err := p.s.Tmpl.AddLateTemplate(self, string(p.Content))
+			_, err := p.s.Tmpl.GetClone().New(self).Parse(string(p.Content))
 			if err != nil {
 				return err
 			}
@@ -1391,7 +1391,6 @@
 			p.selfLayout = self
 		}
 	}
-
 	return nil
 }
 
--- a/hugolib/page_output.go
+++ b/hugolib/page_output.go
@@ -110,29 +110,9 @@
 	l, err := p.layouts(layout...)
 	if err != nil {
 		helpers.DistinctErrorLog.Printf("in .Render: Failed to resolve layout %q for page %q", layout, p.pathOrTitle())
-		return ""
+		return template.HTML("")
 	}
-
-	for _, layout := range l {
-		templ := p.s.Tmpl.Lookup(layout)
-		if templ == nil {
-			// This is legacy from when we had only one output format and
-			// HTML templates only. Some have references to layouts without suffix.
-			// We default to good old HTML.
-			templ = p.s.Tmpl.Lookup(layout + ".html")
-		}
-		if templ != nil {
-			res, err := templ.ExecuteToString(p)
-			if err != nil {
-				helpers.DistinctErrorLog.Printf("in .Render: Failed to execute template %q for page %q", layout, p.pathOrTitle())
-				return template.HTML("")
-			}
-			return template.HTML(res)
-		}
-	}
-
-	return ""
-
+	return p.s.Tmpl.ExecuteTemplateToHTML(p, l...)
 }
 
 func (p *Page) Render(layout ...string) template.HTML {
--- a/hugolib/shortcode.go
+++ b/hugolib/shortcode.go
@@ -177,7 +177,7 @@
 // to avoid potential costly look-aheads for closing tags we look inside the template itself
 // we could change the syntax to self-closing tags, but that would make users cry
 // the value found is cached
-func isInnerShortcode(t tpl.TemplateExecutor) (bool, error) {
+func isInnerShortcode(t *template.Template) (bool, error) {
 	isInnerShortcodeCache.RLock()
 	m, ok := isInnerShortcodeCache.m[t.Name()]
 	isInnerShortcodeCache.RUnlock()
@@ -188,7 +188,10 @@
 
 	isInnerShortcodeCache.Lock()
 	defer isInnerShortcodeCache.Unlock()
-	match, _ := regexp.MatchString("{{.*?\\.Inner.*?}}", t.Tree())
+	if t.Tree == nil {
+		return false, errors.New("Template failed to compile")
+	}
+	match, _ := regexp.MatchString("{{.*?\\.Inner.*?}}", t.Tree.Root.String())
 	isInnerShortcodeCache.m[t.Name()] = match
 
 	return match, nil
@@ -395,6 +398,8 @@
 		case tScName:
 			sc.name = currItem.val
 			tmpl := getShortcodeTemplate(sc.name, p.s.Tmpl)
+			{
+			}
 			if tmpl == nil {
 				return sc, fmt.Errorf("Unable to locate template for shortcode %q in page %q", sc.name, p.Path())
 			}
@@ -565,10 +570,7 @@
 	return source, nil
 }
 
-func getShortcodeTemplate(name string, t tpl.TemplateHandler) *tpl.TemplateAdapter {
-	isInnerShortcodeCache.RLock()
-	defer isInnerShortcodeCache.RUnlock()
-
+func getShortcodeTemplate(name string, t tpl.Template) *template.Template {
 	if x := t.Lookup("shortcodes/" + name + ".html"); x != nil {
 		return x
 	}
@@ -578,7 +580,7 @@
 	return t.Lookup("_internal/shortcodes/" + name + ".html")
 }
 
-func renderShortcodeWithPage(tmpl tpl.Template, data *ShortcodeWithPage) string {
+func renderShortcodeWithPage(tmpl *template.Template, data *ShortcodeWithPage) string {
 	buffer := bp.GetBuffer()
 	defer bp.PutBuffer(buffer)
 
--- a/hugolib/shortcode_test.go
+++ b/hugolib/shortcode_test.go
@@ -30,7 +30,7 @@
 )
 
 // TODO(bep) remove
-func pageFromString(in, filename string, withTemplate ...func(templ tpl.TemplateHandler) 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 @@
 	return s.NewPageFrom(strings.NewReader(in), filename)
 }
 
-func CheckShortCodeMatch(t *testing.T, input, expected string, withTemplate func(templ tpl.TemplateHandler) 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 tpl.TemplateHandler) error, expectError bool) {
+func CheckShortCodeMatchAndError(t *testing.T, input, expected string, withTemplate func(templ tpl.Template) error, expectError bool) {
 
 	cfg, fs := newTestCfg()
 
@@ -100,9 +100,8 @@
 // Issue #929
 func TestHyphenatedSC(t *testing.T) {
 	t.Parallel()
-	wt := func(tem tpl.TemplateHandler) error {
-
-		tem.AddTemplate("_internal/shortcodes/hyphenated-video.html", `Playing Video {{ .Get 0 }}`)
+	wt := func(tem tpl.Template) error {
+		tem.AddInternalShortcode("hyphenated-video.html", `Playing Video {{ .Get 0 }}`)
 		return nil
 	}
 
@@ -112,8 +111,8 @@
 // Issue #1753
 func TestNoTrailingNewline(t *testing.T) {
 	t.Parallel()
-	wt := func(tem tpl.TemplateHandler) error {
-		tem.AddTemplate("_internal/shortcodes/a.html", `{{ .Get 0 }}`)
+	wt := func(tem tpl.Template) error {
+		tem.AddInternalShortcode("a.html", `{{ .Get 0 }}`)
 		return nil
 	}
 
@@ -122,8 +121,8 @@
 
 func TestPositionalParamSC(t *testing.T) {
 	t.Parallel()
-	wt := func(tem tpl.TemplateHandler) error {
-		tem.AddTemplate("_internal/shortcodes/video.html", `Playing Video {{ .Get 0 }}`)
+	wt := func(tem tpl.Template) error {
+		tem.AddInternalShortcode("video.html", `Playing Video {{ .Get 0 }}`)
 		return nil
 	}
 
@@ -136,8 +135,8 @@
 
 func TestPositionalParamIndexOutOfBounds(t *testing.T) {
 	t.Parallel()
-	wt := func(tem tpl.TemplateHandler) error {
-		tem.AddTemplate("_internal/shortcodes/video.html", `Playing Video {{ .Get 1 }}`)
+	wt := func(tem tpl.Template) error {
+		tem.AddInternalShortcode("video.html", `Playing Video {{ .Get 1 }}`)
 		return nil
 	}
 	CheckShortCodeMatch(t, "{{< video 47238zzb >}}", "Playing Video error: index out of range for positional param at position 1", wt)
@@ -147,8 +146,8 @@
 
 func TestNamedParamSC(t *testing.T) {
 	t.Parallel()
-	wt := func(tem tpl.TemplateHandler) error {
-		tem.AddTemplate("_internal/shortcodes/img.html", `<img{{ with .Get "src" }} src="{{.}}"{{end}}{{with .Get "class"}} class="{{.}}"{{end}}>`)
+	wt := func(tem tpl.Template) error {
+		tem.AddInternalShortcode("img.html", `<img{{ with .Get "src" }} src="{{.}}"{{end}}{{with .Get "class"}} class="{{.}}"{{end}}>`)
 		return nil
 	}
 	CheckShortCodeMatch(t, `{{< img src="one" >}}`, `<img src="one">`, wt)
@@ -162,10 +161,10 @@
 // Issue #2294
 func TestNestedNamedMissingParam(t *testing.T) {
 	t.Parallel()
-	wt := func(tem tpl.TemplateHandler) error {
-		tem.AddTemplate("_internal/shortcodes/acc.html", `<div class="acc">{{ .Inner }}</div>`)
-		tem.AddTemplate("_internal/shortcodes/div.html", `<div {{with .Get "class"}} class="{{ . }}"{{ end }}>{{ .Inner }}</div>`)
-		tem.AddTemplate("_internal/shortcodes/div2.html", `<div {{with .Get 0}} class="{{ . }}"{{ end }}>{{ .Inner }}</div>`)
+	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>`)
 		return nil
 	}
 	CheckShortCodeMatch(t,
@@ -175,10 +174,10 @@
 
 func TestIsNamedParamsSC(t *testing.T) {
 	t.Parallel()
-	wt := func(tem tpl.TemplateHandler) error {
-		tem.AddTemplate("_internal/shortcodes/byposition.html", `<div id="{{ .Get 0 }}">`)
-		tem.AddTemplate("_internal/shortcodes/byname.html", `<div id="{{ .Get "id" }}">`)
-		tem.AddTemplate("_internal/shortcodes/ifnamedparams.html", `<div id="{{ if .IsNamedParams }}{{ .Get "id" }}{{ else }}{{ .Get 0 }}{{end}}">`)
+	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}}">`)
 		return nil
 	}
 	CheckShortCodeMatch(t, `{{< ifnamedparams id="name" >}}`, `<div id="name">`, wt)
@@ -191,8 +190,8 @@
 
 func TestInnerSC(t *testing.T) {
 	t.Parallel()
-	wt := func(tem tpl.TemplateHandler) error {
-		tem.AddTemplate("_internal/shortcodes/inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`)
+	wt := func(tem tpl.Template) error {
+		tem.AddInternalShortcode("inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`)
 		return nil
 	}
 	CheckShortCodeMatch(t, `{{< inside class="aspen" >}}`, `<div class="aspen"></div>`, wt)
@@ -202,8 +201,8 @@
 
 func TestInnerSCWithMarkdown(t *testing.T) {
 	t.Parallel()
-	wt := func(tem tpl.TemplateHandler) error {
-		tem.AddTemplate("_internal/shortcodes/inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`)
+	wt := func(tem tpl.Template) error {
+		tem.AddInternalShortcode("inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`)
 		return nil
 	}
 	CheckShortCodeMatch(t, `{{% inside %}}
@@ -216,8 +215,8 @@
 
 func TestInnerSCWithAndWithoutMarkdown(t *testing.T) {
 	t.Parallel()
-	wt := func(tem tpl.TemplateHandler) error {
-		tem.AddTemplate("_internal/shortcodes/inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`)
+	wt := func(tem tpl.Template) error {
+		tem.AddInternalShortcode("inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`)
 		return nil
 	}
 	CheckShortCodeMatch(t, `{{% inside %}}
@@ -247,9 +246,9 @@
 
 func TestNestedSC(t *testing.T) {
 	t.Parallel()
-	wt := func(tem tpl.TemplateHandler) error {
-		tem.AddTemplate("_internal/shortcodes/scn1.html", `<div>Outer, inner is {{ .Inner }}</div>`)
-		tem.AddTemplate("_internal/shortcodes/scn2.html", `<div>SC2</div>`)
+	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
 	}
 	CheckShortCodeMatch(t, `{{% scn1 %}}{{% scn2 %}}{{% /scn1 %}}`, "<div>Outer, inner is <div>SC2</div>\n</div>", wt)
@@ -259,10 +258,10 @@
 
 func TestNestedComplexSC(t *testing.T) {
 	t.Parallel()
-	wt := func(tem tpl.TemplateHandler) error {
-		tem.AddTemplate("_internal/shortcodes/row.html", `-row-{{ .Inner}}-rowStop-`)
-		tem.AddTemplate("_internal/shortcodes/column.html", `-col-{{.Inner    }}-colStop-`)
-		tem.AddTemplate("_internal/shortcodes/aside.html", `-aside-{{    .Inner  }}-asideStop-`)
+	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-`)
 		return nil
 	}
 	CheckShortCodeMatch(t, `{{< row >}}1-s{{% column %}}2-**s**{{< aside >}}3-**s**{{< /aside >}}4-s{{% /column %}}5-s{{< /row >}}6-s`,
@@ -275,10 +274,10 @@
 
 func TestParentShortcode(t *testing.T) {
 	t.Parallel()
-	wt := func(tem tpl.TemplateHandler) error {
-		tem.AddTemplate("_internal/shortcodes/r1.html", `1: {{ .Get "pr1" }} {{ .Inner }}`)
-		tem.AddTemplate("_internal/shortcodes/r2.html", `2: {{ .Parent.Get "pr1" }}{{ .Get "pr2" }} {{ .Inner }}`)
-		tem.AddTemplate("_internal/shortcodes/r3.html", `3: {{ .Parent.Parent.Get "pr1" }}{{ .Parent.Get "pr2" }}{{ .Get "pr3" }} {{ .Inner }}`)
+	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 }}`)
 		return nil
 	}
 	CheckShortCodeMatch(t, `{{< r1 pr1="p1" >}}1: {{< r2 pr2="p2" >}}2: {{< r3 pr3="p3" >}}{{< /r3 >}}{{< /r2 >}}{{< /r1 >}}`,
@@ -343,13 +342,13 @@
 			fmt.Sprintf("Hello %sworld%s. And that's it.", testScPlaceholderRegexp, testScPlaceholderRegexp), ""},
 	} {
 
-		p, _ := pageFromString(simplePage, "simple.md", func(templ tpl.TemplateHandler) error {
-			templ.AddTemplate("_internal/shortcodes/tag.html", `tag`)
-			templ.AddTemplate("_internal/shortcodes/sc1.html", `sc1`)
-			templ.AddTemplate("_internal/shortcodes/sc2.html", `sc2`)
-			templ.AddTemplate("_internal/shortcodes/inner.html", `{{with .Inner }}{{ . }}{{ end }}`)
-			templ.AddTemplate("_internal/shortcodes/inner2.html", `{{.Inner}}`)
-			templ.AddTemplate("_internal/shortcodes/inner3.html", `{{.Inner}}`)
+		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`)
+			templ.AddInternalShortcode("inner.html", `{{with .Inner }}{{ . }}{{ end }}`)
+			templ.AddInternalShortcode("inner2.html", `{{.Inner}}`)
+			templ.AddInternalShortcode("inner3.html", `{{.Inner}}`)
 			return nil
 		})
 
@@ -518,14 +517,14 @@
 		sources[i] = source.ByteSource{Name: filepath.FromSlash(test.contentPath), Content: []byte(test.content)}
 	}
 
-	addTemplates := func(templ tpl.TemplateHandler) error {
+	addTemplates := func(templ tpl.Template) error {
 		templ.AddTemplate("_default/single.html", "{{.Content}}")
 
-		templ.AddTemplate("_internal/shortcodes/b.html", `b`)
-		templ.AddTemplate("_internal/shortcodes/c.html", `c`)
-		templ.AddTemplate("_internal/shortcodes/d.html", `d`)
-		templ.AddTemplate("_internal/shortcodes/menu.html", `{{ len (index .Page.Menus "main").Children }}`)
-		templ.AddTemplate("_internal/shortcodes/tags.html", `{{ len .Page.Site.Taxonomies.tags }}`)
+		templ.AddInternalShortcode("b.html", `b`)
+		templ.AddInternalShortcode("c.html", `c`)
+		templ.AddInternalShortcode("d.html", `d`)
+		templ.AddInternalShortcode("menu.html", `{{ len (index .Page.Menus "main").Children }}`)
+		templ.AddInternalShortcode("tags.html", `{{ len .Page.Site.Taxonomies.tags }}`)
 
 		return nil
 
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -188,7 +188,7 @@
 // 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 tpl.TemplateHandler) error) (*Site, error) {
+func NewSiteDefaultLang(withTemplate ...func(templ tpl.Template) error) (*Site, error) {
 	v := viper.New()
 	loadDefaultSettingsFor(v)
 	return newSiteForLang(helpers.NewDefaultLanguage(v), withTemplate...)
@@ -197,7 +197,7 @@
 // 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 tpl.TemplateHandler) 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...)
@@ -204,8 +204,8 @@
 }
 
 // newSiteForLang creates a new site in the given language.
-func newSiteForLang(lang *helpers.Language, withTemplate ...func(templ tpl.TemplateHandler) error) (*Site, error) {
-	withTemplates := func(templ tpl.TemplateHandler) 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
@@ -1906,14 +1906,13 @@
 }
 
 func (s *Site) renderForLayouts(name string, d interface{}, w io.Writer, layouts ...string) error {
-	templ := s.findFirstTemplate(layouts...)
-	if templ == nil {
+	layout, found := s.findFirstLayout(layouts...)
+	if !found {
 		helpers.DistinctWarnLog.Printf("[%s] Unable to locate layout for %s: %s\n", s.Language.Lang, name, layouts)
-
 		return nil
 	}
 
-	if err := templ.Execute(w, d); err != nil {
+	if err := s.renderThing(d, layout, w); err != nil {
 
 		// Behavior here should be dependent on if running in server or watch mode.
 		helpers.DistinctErrorLog.Printf("Error while rendering %q: %s", name, err)
@@ -1928,13 +1927,23 @@
 	return nil
 }
 
-func (s *Site) findFirstTemplate(layouts ...string) tpl.Template {
+func (s *Site) findFirstLayout(layouts ...string) (string, bool) {
 	for _, layout := range layouts {
-		if templ := s.Tmpl.Lookup(layout); templ != nil {
-			return templ
+		if s.Tmpl.Lookup(layout) != nil {
+			return layout, true
 		}
 	}
-	return nil
+	return "", false
+}
+
+func (s *Site) renderThing(d interface{}, layout string, w io.Writer) error {
+
+	// If the template doesn't exist, then return, but leave the Writer open
+	if templ := s.Tmpl.Lookup(layout); templ != nil {
+		return templ.Execute(w, d)
+	}
+	return fmt.Errorf("Layout not found: %s", layout)
+
 }
 
 func (s *Site) publish(path string, r io.Reader) (err error) {
--- a/hugolib/site_output_test.go
+++ b/hugolib/site_output_test.go
@@ -90,15 +90,9 @@
 Alt Output: {{ .Name -}}|
 {{- end -}}|
 {{- range .OutputFormats -}}
-Output/Rel: {{ .Name -}}/{{ .Rel }}|{{ .MediaType }}
+Output/Rel: {{ .Name -}}/{{ .Rel }}|
 {{- end -}}
- {{ with .OutputFormats.Get "JSON" }}
-<atom:link href={{ .Permalink }} rel="self" type="{{ .MediaType }}" />
-{{ end }}
 `,
-		"layouts/_default/list.html", `List HTML|{{ with .OutputFormats.Get "HTML" -}}
-<atom:link href={{ .Permalink }} rel="self" type="{{ .MediaType }}" />
-{{- end -}}`,
 	)
 	require.Len(t, h.Sites, 1)
 
@@ -119,6 +113,7 @@
 
 	require.Len(t, home.outputFormats, lenOut)
 
+	// TODO(bep) output assert template/text
 	// There is currently always a JSON output to make it simpler ...
 	altFormats := lenOut - 1
 	hasHTML := helpers.InStringArray(outputs, "html")
@@ -133,16 +128,9 @@
 			"Output/Rel: JSON/alternate|",
 			"Output/Rel: HTML/canonical|",
 		)
-		th.assertFileContent("public/index.html",
-			// The HTML entity is a deliberate part of this test: The HTML templates are
-			// parsed with html/template.
-			`List HTML|<atom:link href=http://example.com/blog/ rel="self" type="text/html&#43;html" />`,
-		)
 	} else {
 		th.assertFileContent("public/index.json",
 			"Output/Rel: JSON/canonical|",
-			// JSON is plain text, so no need to safeHTML this and that
-			`<atom:link href=http://example.com/blog/index.json rel="self" type="application/json+json" />`,
 		)
 	}
 
--- a/hugolib/site_test.go
+++ b/hugolib/site_test.go
@@ -52,6 +52,24 @@
 	return p
 }
 
+func TestDegenerateRenderThingMissingTemplate(t *testing.T) {
+	t.Parallel()
+	cfg, fs := newTestCfg()
+
+	writeSource(t, fs, filepath.Join("content", "a", "file.md"), pageSimpleTitle)
+
+	s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
+
+	require.Len(t, s.RegularPages, 1)
+
+	p := s.RegularPages[0]
+
+	err := s.renderThing(p, "foobar", nil)
+	if err == nil {
+		t.Errorf("Expected err to be returned when missing the template.")
+	}
+}
+
 func TestRenderWithInvalidTemplate(t *testing.T) {
 	t.Parallel()
 	cfg, fs := newTestCfg()
--- a/hugolib/sitemap_test.go
+++ b/hugolib/sitemap_test.go
@@ -48,7 +48,7 @@
 	depsCfg := deps.DepsCfg{Fs: fs, Cfg: cfg}
 
 	if !internal {
-		depsCfg.WithTemplate = func(templ tpl.TemplateHandler) error {
+		depsCfg.WithTemplate = func(templ tpl.Template) error {
 			templ.AddTemplate("sitemap.xml", sitemapTemplate)
 			return nil
 		}
--- a/hugolib/testhelpers_test.go
+++ b/hugolib/testhelpers_test.go
@@ -164,9 +164,9 @@
 func newErrorLogger() *jww.Notepad {
 	return jww.NewNotepad(jww.LevelError, jww.LevelError, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
 }
-func createWithTemplateFromNameValues(additionalTemplates ...string) func(templ tpl.TemplateHandler) error {
+func createWithTemplateFromNameValues(additionalTemplates ...string) func(templ tpl.Template) error {
 
-	return func(templ tpl.TemplateHandler) 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 {
--- a/output/layout.go
+++ b/output/layout.go
@@ -152,11 +152,9 @@
 			}
 		}
 
-		layouts = layoutsWithThemeLayouts
+		return layoutsWithThemeLayouts, nil
 	}
 
-	layouts = prependTextPrefixIfNeeded(f, layouts...)
-
 	l.mu.Lock()
 	l.cache[key] = layouts
 	l.mu.Unlock()
@@ -186,36 +184,18 @@
 }
 
 func resolveTemplate(templ string, d LayoutDescriptor, f Format) []string {
-	layouts := strings.Fields(replaceKeyValues(templ,
+	return strings.Fields(replaceKeyValues(templ,
 		"SUFFIX", f.MediaType.Suffix,
 		"NAME", strings.ToLower(f.Name),
 		"SECTION", d.Section))
-
-	return layouts
 }
 
-func prependTextPrefixIfNeeded(f Format, layouts ...string) []string {
-	if !f.IsPlainText {
-		return layouts
-	}
-
-	newLayouts := make([]string, len(layouts))
-
-	for i, l := range layouts {
-		newLayouts[i] = "_text/" + l
-	}
-
-	return newLayouts
-}
-
 func replaceKeyValues(s string, oldNew ...string) string {
 	replacer := strings.NewReplacer(oldNew...)
 	return replacer.Replace(s)
 }
 
-func regularPageLayouts(types string, layout string, f Format) []string {
-	var layouts []string
-
+func regularPageLayouts(types string, layout string, f Format) (layouts []string) {
 	if layout == "" {
 		layout = "single"
 	}
@@ -239,5 +219,5 @@
 	layouts = append(layouts, fmt.Sprintf("_default/%s.%s.%s", layout, name, suffix))
 	layouts = append(layouts, fmt.Sprintf("_default/%s.%s", layout, suffix))
 
-	return layouts
+	return
 }
--- a/output/layout_base.go
+++ b/output/layout_base.go
@@ -29,10 +29,7 @@
 )
 
 type TemplateNames struct {
-	// The name used as key in the template map. Note that this will be
-	// prefixed with "_text/" if it should be parsed with text/template.
-	Name string
-
+	Name            string
 	OverlayFilename string
 	MasterFilename  string
 }
@@ -54,10 +51,6 @@
 	// The theme name if active.
 	Theme string
 
-	// All the output formats in play. This is used to decide if text/template or
-	// html/template.
-	OutputFormats Formats
-
 	FileExists  func(filename string) (bool, error)
 	ContainsAny func(filename string, subslices [][]byte) (bool, error)
 }
@@ -81,13 +74,7 @@
 	// index.amp.html
 	// index.json
 	filename := filepath.Base(d.RelPath)
-	isPlainText := false
-	outputFormat, found := d.OutputFormats.FromFilename(filename)
 
-	if found && outputFormat.IsPlainText {
-		isPlainText = true
-	}
-
 	var ext, outFormat string
 
 	parts := strings.Split(filename, ".")
@@ -102,10 +89,6 @@
 
 	id.OverlayFilename = fullPath
 	id.Name = name
-
-	if isPlainText {
-		id.Name = "_text/" + id.Name
-	}
 
 	// Ace and Go templates may have both a base and inner template.
 	pathDir := filepath.Dir(fullPath)
--- a/output/layout_base_test.go
+++ b/output/layout_base_test.go
@@ -141,7 +141,6 @@
 				return this.needsBase, nil
 			}
 
-			this.d.OutputFormats = Formats{AMPFormat, HTMLFormat, RSSFormat, JSONFormat}
 			this.d.WorkingDir = filepath.FromSlash(this.d.WorkingDir)
 			this.d.LayoutDir = filepath.FromSlash(this.d.LayoutDir)
 			this.d.RelPath = filepath.FromSlash(this.d.RelPath)
@@ -150,11 +149,6 @@
 
 			this.expect.MasterFilename = filepath.FromSlash(this.expect.MasterFilename)
 			this.expect.OverlayFilename = filepath.FromSlash(this.expect.OverlayFilename)
-
-			if strings.Contains(this.d.RelPath, "json") {
-				// currently the only plain text templates in this test.
-				this.expect.Name = "_text/" + this.expect.Name
-			}
 
 			id, err := CreateTemplateNames(this.d)
 
--- a/output/layout_test.go
+++ b/output/layout_test.go
@@ -64,10 +64,6 @@
 			[]string{"taxonomy/tag.rss.xml", "_default/rss.xml", "rss.xml", "_internal/_default/rss.xml"}},
 		{"RSS Taxonomy term", LayoutDescriptor{Kind: "taxonomyTerm", Section: "tag"}, false, "", RSSFormat,
 			[]string{"taxonomy/tag.terms.rss.xml", "_default/rss.xml", "rss.xml", "_internal/_default/rss.xml"}},
-		{"Home plain text", LayoutDescriptor{Kind: "home"}, true, "", JSONFormat,
-			[]string{"_text/index.json.json", "_text/index.json", "_text/_default/list.json.json", "_text/_default/list.json", "_text/theme/index.json.json", "_text/theme/index.json"}},
-		{"Page plain text", LayoutDescriptor{Kind: "page"}, true, "", JSONFormat,
-			[]string{"_text/_default/single.json.json", "_text/_default/single.json", "_text/theme/_default/single.json.json"}},
 	} {
 		t.Run(this.name, func(t *testing.T) {
 			l := NewLayoutHandler(this.hasTheme)
--- a/output/outputFormat.go
+++ b/output/outputFormat.go
@@ -33,7 +33,6 @@
 		IsHTML:    true,
 	}
 
-	// CalendarFormat is AAA
 	CalendarFormat = Format{
 		Name:        "Calendar",
 		MediaType:   media.CalendarType,
@@ -101,45 +100,6 @@
 			found = true
 			return
 		}
-	}
-	return
-}
-
-func (formats Formats) GetBySuffix(name string) (f Format, found bool) {
-	for _, ff := range formats {
-		if name == ff.MediaType.Suffix {
-			if found {
-				// ambiguous
-				found = false
-				return
-			}
-			f = ff
-			found = true
-		}
-	}
-	return
-}
-
-func (formats Formats) FromFilename(filename string) (f Format, found bool) {
-	// mytemplate.amp.html
-	// mytemplate.html
-	// mytemplate
-	var ext, outFormat string
-
-	parts := strings.Split(filename, ".")
-	if len(parts) > 2 {
-		outFormat = parts[1]
-		ext = parts[2]
-	} else if len(parts) > 1 {
-		ext = parts[1]
-	}
-
-	if outFormat != "" {
-		return formats.GetByName(outFormat)
-	}
-
-	if ext != "" {
-		return formats.GetBySuffix(ext)
 	}
 	return
 }
--- a/output/outputFormat_test.go
+++ b/output/outputFormat_test.go
@@ -65,36 +65,11 @@
 
 }
 
-func TestGetFormat(t *testing.T) {
+func TestGetType(t *testing.T) {
 	tp, _ := GetFormat("html")
 	require.Equal(t, HTMLFormat, tp)
 	tp, _ = GetFormat("HTML")
 	require.Equal(t, HTMLFormat, tp)
 	_, found := GetFormat("FOO")
-	require.False(t, found)
-}
-
-func TestGeGetFormatByName(t *testing.T) {
-	formats := Formats{AMPFormat, CalendarFormat}
-	tp, _ := formats.GetByName("AMP")
-	require.Equal(t, AMPFormat, tp)
-	_, found := formats.GetByName("HTML")
-	require.False(t, found)
-	_, found = formats.GetByName("FOO")
-	require.False(t, found)
-}
-
-func TestGeGetFormatByExt(t *testing.T) {
-	formats1 := Formats{AMPFormat, CalendarFormat}
-	formats2 := Formats{AMPFormat, HTMLFormat, CalendarFormat}
-	tp, _ := formats1.GetBySuffix("html")
-	require.Equal(t, AMPFormat, tp)
-	tp, _ = formats1.GetBySuffix("ics")
-	require.Equal(t, CalendarFormat, tp)
-	_, found := formats1.GetBySuffix("not")
-	require.False(t, found)
-
-	// ambiguous
-	_, found = formats2.GetByName("html")
 	require.False(t, found)
 }
--- a/tpl/template.go
+++ b/tpl/template.go
@@ -1,103 +1,28 @@
-// 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 (
-	"io"
-
-	"text/template/parse"
-
 	"html/template"
-	texttemplate "text/template"
-
-	bp "github.com/spf13/hugo/bufferpool"
+	"io"
 )
 
-var (
-	_ TemplateExecutor = (*TemplateAdapter)(nil)
-)
-
-// TemplateHandler manages the collection of templates.
-type TemplateHandler interface {
-	TemplateFinder
+// 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)
 	AddTemplate(name, tpl string) error
-	AddLateTemplate(name, tpl string) error
-	LoadTemplates(absPath, prefix string)
+	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()
-	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{})
 }
--- a/tpl/tplimpl/ace.go
+++ /dev/null
@@ -1,51 +1,0 @@
-// 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 tplimpl
-
-import (
-	"path/filepath"
-
-	"strings"
-
-	"github.com/yosssi/ace"
-)
-
-func (t *templateHandler) 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.html.t.New(name), parsed, nil)
-	if err != nil {
-		t.errors = append(t.errors, &templateErr{name: name, err: err})
-		return err
-	}
-	return applyTemplateTransformersToHMLTTemplate(templ)
-}
--- a/tpl/tplimpl/amber_compiler.go
+++ b/tpl/tplimpl/amber_compiler.go
@@ -19,7 +19,7 @@
 	"github.com/eknkc/amber"
 )
 
-func (t *templateHandler) compileAmberWithTemplate(b []byte, path string, templ *template.Template) (*template.Template, error) {
+func (gt *GoHTMLTemplate) CompileAmberWithTemplate(b []byte, path string, t *template.Template) (*template.Template, error) {
 	c := amber.New()
 
 	if err := c.ParseData(b, path); err != nil {
@@ -32,7 +32,7 @@
 		return nil, err
 	}
 
-	tpl, err := templ.Funcs(t.amberFuncMap).Parse(data)
+	tpl, err := t.Funcs(gt.amberFuncMap).Parse(data)
 
 	if err != nil {
 		return nil, err
--- a/tpl/tplimpl/template.go
+++ b/tpl/tplimpl/template.go
@@ -1,4 +1,4 @@
-// Copyright 2017-present The Hugo Authors. All rights reserved.
+// 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.
@@ -15,40 +15,24 @@
 
 import (
 	"html/template"
-	"strings"
-	texttemplate "text/template"
-
-	"github.com/eknkc/amber"
-
+	"io"
 	"os"
-
-	"github.com/spf13/hugo/output"
-
 	"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/spf13/hugo/tpl"
+	"github.com/spf13/hugo/output"
+	"github.com/yosssi/ace"
 )
 
-const (
-	textTmplNamePrefix = "_text/"
-)
+// TODO(bep) globals get rid of the rest of the jww.ERR etc.
 
-var (
-	_ tpl.TemplateHandler       = (*templateHandler)(nil)
-	_ tpl.TemplateTestMocker    = (*templateHandler)(nil)
-	_ tpl.TemplateFinder        = (*htmlTemplates)(nil)
-	_ tpl.TemplateFinder        = (*textTemplates)(nil)
-	_ templateLoader            = (*htmlTemplates)(nil)
-	_ templateLoader            = (*textTemplates)(nil)
-	_ templateLoader            = (*templateHandler)(nil)
-	_ templateFuncsterTemplater = (*htmlTemplates)(nil)
-	_ templateFuncsterTemplater = (*textTemplates)(nil)
-)
-
 // Protecting global map access (Amber)
 var amberMu sync.Mutex
 
@@ -57,189 +41,177 @@
 	err  error
 }
 
-type templateLoader interface {
-	handleMaster(name, overlayFilename, masterFilename string, onMissing func(filename string) (string, error)) error
-	addTemplate(name, tpl string) error
-	addLateTemplate(name, tpl string) error
-}
+type GoHTMLTemplate struct {
+	*template.Template
 
-type templateFuncsterTemplater interface {
-	tpl.TemplateFinder
-	setFuncs(funcMap map[string]interface{})
-	setTemplateFuncster(f *templateFuncster)
-}
+	// This looks, and is, strange.
+	// The clone is used by non-renderable content pages, and these need to be
+	// re-parsed on content change, and to avoid the
+	// "cannot Parse after Execute" error, we need to re-clone it from the original clone.
+	clone      *template.Template
+	cloneClone *template.Template
 
-// templateHandler holds the templates in play.
-// It implements the templateLoader and tpl.TemplateHandler interfaces.
-type templateHandler struct {
-	// text holds all the pure text templates.
-	text *textTemplates
-	html *htmlTemplates
+	// 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
 
-	amberFuncMap template.FuncMap
-
 	errors []*templateErr
 
+	funcster *templateFuncster
+
+	amberFuncMap template.FuncMap
+
 	*deps.Deps
 }
 
-func (t *templateHandler) addError(name string, err error) {
-	t.errors = append(t.errors, &templateErr{name, err})
-}
+type TemplateProvider struct{}
 
-// PrintErrors prints the accumulated errors as ERROR to the log.
-func (t *templateHandler) PrintErrors() {
-	for _, e := range t.errors {
-		t.Log.ERROR.Println(e.name, ":", e.err)
+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 {
+	tmpl := &GoHTMLTemplate{
+		Template: template.New(""),
+		overlays: make(map[string]*template.Template),
+		errors:   make([]*templateErr, 0),
+		Deps:     deps,
 	}
-}
 
-// 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 {
-	var te *tpl.TemplateAdapter
+	deps.Tmpl = tmpl
 
-	isTextTemplate := strings.HasPrefix(name, textTmplNamePrefix)
+	tmpl.initFuncs(deps)
 
-	if isTextTemplate {
-		// The templates are stored without the prefix identificator.
-		name = strings.TrimPrefix(name, textTmplNamePrefix)
-		te = t.text.Lookup(name)
-	} else {
-		te = t.html.Lookup(name)
-	}
+	tmpl.LoadEmbedded()
 
-	if te == nil {
-		return nil
+	if deps.WithTemplate != nil {
+		err := deps.WithTemplate(tmpl)
+		if err != nil {
+			tmpl.errors = append(tmpl.errors, &templateErr{"init", err})
+		}
+
 	}
 
-	return te
+	tmpl.MarkReady()
+
+	return nil
+
 }
 
-func (t *templateHandler) clone(d *deps.Deps) *templateHandler {
-	c := &templateHandler{
-		Deps:   d,
-		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)},
-		errors: make([]*templateErr, 0),
-	}
+// Clone clones
+func (*TemplateProvider) Clone(d *deps.Deps) error {
 
-	d.Tmpl = c
+	t := d.Tmpl.(*GoHTMLTemplate)
 
-	c.initFuncs()
+	// 1. Clone the clone with new template funcs
+	// 2. Clone any overlays with new template funcs
 
-	for k, v := range t.html.overlays {
+	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(t.html.funcster.funcMap)
-		c.html.overlays[k] = vc
+		vc.Funcs(tmpl.funcster.funcMap)
+		tmpl.overlays[k] = vc
 	}
 
-	for k, v := range t.text.overlays {
-		vc := texttemplate.Must(v.Clone())
-		vc = vc.Lookup(vc.Name())
-		vc.Funcs(texttemplate.FuncMap(t.text.funcster.funcMap))
-		c.text.overlays[k] = vc
-	}
+	tmpl.MarkReady()
 
-	return c
-
+	return nil
 }
 
-func newTemplateAdapter(deps *deps.Deps) *templateHandler {
-	htmlT := &htmlTemplates{
-		t:        template.New(""),
-		overlays: make(map[string]*template.Template),
-	}
-	textT := &textTemplates{
-		t:        texttemplate.New(""),
-		overlays: make(map[string]*texttemplate.Template),
-	}
-	return &templateHandler{
-		Deps:   deps,
-		html:   htmlT,
-		text:   textT,
-		errors: make([]*templateErr, 0),
-	}
+func (t *GoHTMLTemplate) initFuncs(d *deps.Deps) {
 
-}
+	t.funcster = newTemplateFuncster(d)
 
-type htmlTemplates struct {
-	funcster *templateFuncster
+	// 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 *template.Template
+	t.amberFuncMap = template.FuncMap{}
 
-	// This looks, and is, strange.
-	// The clone is used by non-renderable content pages, and these need to be
-	// re-parsed on content change, and to avoid the
-	// "cannot Parse after Execute" error, we need to re-clone it from the original clone.
-	clone      *template.Template
-	cloneClone *template.Template
+	amberMu.Lock()
+	for k, v := range amber.FuncMap {
+		t.amberFuncMap[k] = v
+	}
 
-	// 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
-}
+	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 *htmlTemplates) setTemplateFuncster(f *templateFuncster) {
-	t.funcster = f
 }
 
-func (t *htmlTemplates) Lookup(name string) *tpl.TemplateAdapter {
-	templ := t.lookup(name)
-	if templ == nil {
-		return nil
-	}
-	return &tpl.TemplateAdapter{templ}
+func (t *GoHTMLTemplate) Funcs(funcMap template.FuncMap) {
+	t.Template.Funcs(funcMap)
 }
 
-func (t *htmlTemplates) lookup(name string) *template.Template {
-	if templ := t.t.Lookup(name); templ != nil {
-		return templ
+func (t *GoHTMLTemplate) Partial(name string, contextList ...interface{}) template.HTML {
+	if strings.HasPrefix("partials/", name) {
+		name = name[8:]
 	}
-	if t.overlays != nil {
-		if templ, ok := t.overlays[name]; ok {
-			return templ
-		}
-	}
+	var context interface{}
 
-	if t.clone != nil {
-		return t.clone.Lookup(name)
+	if len(contextList) == 0 {
+		context = nil
+	} else {
+		context = contextList[0]
 	}
-
-	return nil
+	return t.ExecuteTemplateToHTML(context, "partials/"+name, "theme/partials/"+name)
 }
 
-type textTemplates struct {
-	funcster *templateFuncster
+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 {
+			// TODO(bep) output
+			layout += ".html"
+			templ = t.Lookup(layout)
+		}
 
-	t *texttemplate.Template
-
-	clone      *texttemplate.Template
-	cloneClone *texttemplate.Template
-
-	overlays map[string]*texttemplate.Template
+		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 *textTemplates) setTemplateFuncster(f *templateFuncster) {
-	t.funcster = f
+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 *textTemplates) Lookup(name string) *tpl.TemplateAdapter {
-	templ := t.lookup(name)
-	if templ == nil {
-		return nil
-	}
-	return &tpl.TemplateAdapter{templ}
-}
+func (t *GoHTMLTemplate) Lookup(name string) *template.Template {
 
-func (t *textTemplates) lookup(name string) *texttemplate.Template {
-	if templ := t.t.Lookup(name); templ != nil {
+	if templ := t.Template.Lookup(name); templ != nil {
 		return templ
 	}
+
 	if t.overlays != nil {
 		if templ, ok := t.overlays[name]; ok {
 			return templ
@@ -246,70 +218,68 @@
 		}
 	}
 
+	// 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 {
-		return t.clone.Lookup(name)
+		if templ := t.clone.Lookup(name); templ != nil {
+			return templ
+		}
 	}
 
 	return nil
-}
 
-func (t *templateHandler) setFuncs(funcMap map[string]interface{}) {
-	t.html.setFuncs(funcMap)
-	t.text.setFuncs(funcMap)
 }
 
-// SetFuncs replaces the funcs in the func maps with new definitions.
-// This is only used in tests.
-func (t *templateHandler) SetFuncs(funcMap map[string]interface{}) {
-	t.setFuncs(funcMap)
+func (t *GoHTMLTemplate) GetClone() *template.Template {
+	return t.clone
 }
 
-func (t *htmlTemplates) setFuncs(funcMap map[string]interface{}) {
-	t.t.Funcs(funcMap)
+func (t *GoHTMLTemplate) RebuildClone() *template.Template {
+	t.clone = template.Must(t.cloneClone.Clone())
+	return t.clone
 }
 
-func (t *textTemplates) setFuncs(funcMap map[string]interface{}) {
-	t.t.Funcs(funcMap)
+func (t *GoHTMLTemplate) LoadEmbedded() {
+	t.EmbedShortcodes()
+	t.EmbedTemplates()
 }
 
-// LoadTemplates loads the templates, starting from the given absolute path.
-// A prefix can be given to indicate a template namespace to load the templates
-// into, i.e. "_internal" etc.
-func (t *templateHandler) LoadTemplates(absPath, prefix string) {
-	// TODO(bep) output formats. Will have to get to complete list when that is ready.
-	t.loadTemplates(absPath, prefix, output.Formats{output.HTMLFormat, output.RSSFormat, output.CalendarFormat, output.AMPFormat, output.JSONFormat})
-
+// MarkReady marks the template as "ready for execution". No changes allowed
+// after this is set.
+// TODO(bep) if this proves to be resource heavy, we could detect
+// earlier if we really need this, or make it lazy.
+func (t *GoHTMLTemplate) MarkReady() {
+	if t.clone == nil {
+		t.clone = template.Must(t.Template.Clone())
+		t.cloneClone = template.Must(t.clone.Clone())
+	}
 }
 
-func (t *htmlTemplates) addTemplateIn(tt *template.Template, name, tpl string) error {
-	templ, err := tt.New(name).Parse(tpl)
-	if err != nil {
-		return err
+func (t *GoHTMLTemplate) checkState() {
+	if t.clone != nil {
+		panic("template is cloned and cannot be modfified")
 	}
+}
 
-	if err := applyTemplateTransformersToHMLTTemplate(templ); err != nil {
-		return err
+func (t *GoHTMLTemplate) AddInternalTemplate(prefix, name, tpl string) error {
+	if prefix != "" {
+		return t.AddTemplate("_internal/"+prefix+"/"+name, tpl)
 	}
-
-	return nil
+	return t.AddTemplate("_internal/"+name, tpl)
 }
 
-func (t *htmlTemplates) addTemplate(name, tpl string) error {
-	return t.addTemplateIn(t.t, name, tpl)
+func (t *GoHTMLTemplate) AddInternalShortcode(name, content string) error {
+	return t.AddInternalTemplate("shortcodes", name, content)
 }
 
-func (t *htmlTemplates) addLateTemplate(name, tpl string) error {
-	return t.addTemplateIn(t.clone, name, tpl)
-}
-
-func (t *textTemplates) addTemplateIn(tt *texttemplate.Template, name, tpl string) error {
-	name = strings.TrimPrefix(name, textTmplNamePrefix)
-	templ, err := tt.New(name).Parse(tpl)
+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 := applyTemplateTransformersToTextTemplate(templ); err != nil {
+	if err := applyTemplateTransformers(templ); err != nil {
 		return err
 	}
 
@@ -316,280 +286,95 @@
 	return nil
 }
 
-func (t *textTemplates) addTemplate(name, tpl string) error {
-	return t.addTemplateIn(t.t, name, tpl)
-}
+func (t *GoHTMLTemplate) AddTemplateFileWithMaster(name, overlayFilename, masterFilename string) error {
 
-func (t *textTemplates) addLateTemplate(name, tpl string) error {
-	return t.addTemplateIn(t.clone, name, tpl)
-}
+	// 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
 
-func (t *templateHandler) addTemplate(name, tpl string) error {
-	return t.AddTemplate(name, tpl)
-}
+	masterTpl := t.Lookup(masterFilename)
 
-func (t *templateHandler) addLateTemplate(name, tpl string) error {
-	return t.AddLateTemplate(name, tpl)
-}
-
-// AddLateTemplate is used to add a template late, i.e. after the
-// regular templates have started its execution.
-func (t *templateHandler) AddLateTemplate(name, tpl string) error {
-	h := t.getTemplateHandler(name)
-	if err := h.addLateTemplate(name, tpl); err != nil {
-		t.addError(name, err)
-		return err
-	}
-	return nil
-}
-
-// AddTemplate parses and adds a template to the collection.
-// Templates with name prefixed with "_text" will be handled as plain
-// text templates.
-func (t *templateHandler) AddTemplate(name, tpl string) error {
-	h := t.getTemplateHandler(name)
-	if err := h.addTemplate(name, tpl); err != nil {
-		t.addError(name, err)
-		return err
-	}
-	return nil
-}
-
-// MarkReady marks the templates as "ready for execution". No changes allowed
-// after this is set.
-// TODO(bep) if this proves to be resource heavy, we could detect
-// earlier if we really need this, or make it lazy.
-func (t *templateHandler) MarkReady() {
-	if t.html.clone == nil {
-		t.html.clone = template.Must(t.html.t.Clone())
-		t.html.cloneClone = template.Must(t.html.clone.Clone())
-	}
-	if t.text.clone == nil {
-		t.text.clone = texttemplate.Must(t.text.t.Clone())
-		t.text.cloneClone = texttemplate.Must(t.text.clone.Clone())
-	}
-}
-
-// RebuildClone rebuilds the cloned templates. Used for live-reloads.
-func (t *templateHandler) RebuildClone() {
-	t.html.clone = template.Must(t.html.cloneClone.Clone())
-	t.text.clone = texttemplate.Must(t.text.cloneClone.Clone())
-}
-
-func (t *templateHandler) loadTemplates(absPath string, prefix string, formats output.Formats) {
-	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
-			}
-
-			var (
-				workingDir = t.PathSpec.WorkingDir()
-				themeDir   = t.PathSpec.GetThemeDir()
-				layoutDir  = t.PathSpec.LayoutDir()
-			)
-
-			if themeDir != "" && strings.HasPrefix(absPath, themeDir) {
-				workingDir = themeDir
-				layoutDir = "layouts"
-			}
-
-			li := strings.LastIndex(path, layoutDir) + len(layoutDir) + 1
-			relPath := path[li:]
-
-			descriptor := output.TemplateLookupDescriptor{
-				WorkingDir:    workingDir,
-				LayoutDir:     layoutDir,
-				RelPath:       relPath,
-				Prefix:        prefix,
-				Theme:         t.PathSpec.Theme(),
-				OutputFormats: formats,
-				FileExists: func(filename string) (bool, error) {
-					return helpers.Exists(filename, t.Fs.Source)
-				},
-				ContainsAny: func(filename string, subslices [][]byte) (bool, error) {
-					return helpers.FileContainsAny(filename, subslices, t.Fs.Source)
-				},
-			}
-
-			tplID, err := output.CreateTemplateNames(descriptor)
-			if err != nil {
-				t.Log.ERROR.Printf("Failed to resolve template in path %q: %s", path, err)
-
-				return nil
-			}
-
-			if err := t.addTemplateFile(tplID.Name, tplID.MasterFilename, tplID.OverlayFilename); err != nil {
-				t.Log.ERROR.Printf("Failed to add template %q in path %q: %s", tplID.Name, 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 (t *templateHandler) initFuncs() {
-
-	// The template funcs need separation between text and html templates.
-	for _, funcsterHolder := range []templateFuncsterTemplater{t.html, t.text} {
-		funcster := newTemplateFuncster(t.Deps, funcsterHolder)
-
-		// 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()
-
-		funcsterHolder.setTemplateFuncster(funcster)
-
-	}
-
-	// Amber is HTML only.
-	t.amberFuncMap = template.FuncMap{}
-
-	amberMu.Lock()
-	for k, v := range amber.FuncMap {
-		t.amberFuncMap[k] = v
-	}
-
-	for k, v := range t.html.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 *templateHandler) getTemplateHandler(name string) templateLoader {
-	if strings.HasPrefix(name, textTmplNamePrefix) {
-		return t.text
-	}
-	return t.html
-}
-
-func (t *templateHandler) handleMaster(name, overlayFilename, masterFilename string, onMissing func(filename string) (string, error)) error {
-	h := t.getTemplateHandler(name)
-	return h.handleMaster(name, overlayFilename, masterFilename, onMissing)
-}
-
-func (t *htmlTemplates) handleMaster(name, overlayFilename, masterFilename string, onMissing func(filename string) (string, error)) error {
-	masterTpl := t.lookup(masterFilename)
-
 	if masterTpl == nil {
-		templ, err := onMissing(masterFilename)
+		b, err := afero.ReadFile(t.Fs.Source, masterFilename)
 		if err != nil {
 			return err
 		}
+		masterTpl, err = t.New(masterFilename).Parse(string(b))
 
-		masterTpl, err = t.t.New(overlayFilename).Parse(templ)
 		if err != nil {
+			// TODO(bep) Add a method that does this
+			t.errors = append(t.errors, &templateErr{name: name, err: err})
 			return err
 		}
 	}
 
-	templ, err := onMissing(overlayFilename)
+	b, err := afero.ReadFile(t.Fs.Source, overlayFilename)
 	if err != nil {
 		return err
 	}
 
-	overlayTpl, err := template.Must(masterTpl.Clone()).Parse(templ)
+	overlayTpl, err := template.Must(masterTpl.Clone()).Parse(string(b))
 	if err != nil {
-		return err
+		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
 	}
 
-	// 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 := applyTemplateTransformersToHMLTTemplate(overlayTpl); err != nil {
-		return err
-	}
-	t.overlays[name] = overlayTpl
-
 	return err
-
 }
 
-func (t *textTemplates) handleMaster(name, overlayFilename, masterFilename string, onMissing func(filename string) (string, error)) error {
-	masterTpl := t.lookup(masterFilename)
+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"
 
-	if masterTpl == nil {
-		templ, err := onMissing(masterFilename)
-		if err != nil {
-			return err
-		}
+	// Fixes issue #1178
+	basePath = strings.Replace(basePath, "\\", "/", -1)
+	innerPath = strings.Replace(innerPath, "\\", "/", -1)
 
-		masterTpl, err = t.t.New(overlayFilename).Parse(templ)
-		if err != nil {
-			return err
-		}
+	if basePath != "" {
+		base = ace.NewFile(basePath, baseContent)
+		inner = ace.NewFile(innerPath, innerContent)
+	} else {
+		base = ace.NewFile(innerPath, innerContent)
+		inner = ace.NewFile("", []byte{})
 	}
-
-	templ, err := onMissing(overlayFilename)
+	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
 	}
-
-	overlayTpl, err := texttemplate.Must(masterTpl.Clone()).Parse(templ)
+	templ, err := ace.CompileResultWithTemplate(t.New(name), parsed, nil)
 	if err != nil {
+		t.errors = append(t.errors, &templateErr{name: name, err: err})
 		return err
 	}
-
-	overlayTpl = overlayTpl.Lookup(overlayTpl.Name())
-	if err := applyTemplateTransformersToTextTemplate(overlayTpl); err != nil {
-		return err
-	}
-	t.overlays[name] = overlayTpl
-
-	return err
-
+	return applyTemplateTransformers(templ)
 }
 
-func (t *templateHandler) addTemplateFile(name, baseTemplatePath, path string) error {
+func (t *GoHTMLTemplate) AddTemplateFile(name, baseTemplatePath, path string) error {
 	t.checkState()
-
-	getTemplate := func(filename string) (string, error) {
-		b, err := afero.ReadFile(t.Fs.Source, filename)
-		if err != nil {
-			return "", err
-		}
-		return string(b), nil
-	}
-
 	// get the suffix and switch on that
 	ext := filepath.Ext(path)
 	switch ext {
 	case ".amber":
-		//	Only HTML support for Amber
 		templateName := strings.TrimSuffix(name, filepath.Ext(name)) + ".html"
 		b, err := afero.ReadFile(t.Fs.Source, path)
 
@@ -598,15 +383,14 @@
 		}
 
 		amberMu.Lock()
-		templ, err := t.compileAmberWithTemplate(b, path, t.html.t.New(templateName))
+		templ, err := t.CompileAmberWithTemplate(b, path, t.New(templateName))
 		amberMu.Unlock()
 		if err != nil {
 			return err
 		}
 
-		return applyTemplateTransformersToHMLTTemplate(templ)
+		return applyTemplateTransformers(templ)
 	case ".ace":
-		//	Only HTML support for Ace
 		var innerContent, baseContent []byte
 		innerContent, err := afero.ReadFile(t.Fs.Source, path)
 
@@ -621,14 +405,14 @@
 			}
 		}
 
-		return t.addAceTemplate(name, baseTemplatePath, path, baseContent, innerContent)
+		return t.AddAceTemplate(name, baseTemplatePath, path, baseContent, innerContent)
 	default:
 
 		if baseTemplatePath != "" {
-			return t.handleMaster(name, path, baseTemplatePath, getTemplate)
+			return t.AddTemplateFileWithMaster(name, path, baseTemplatePath)
 		}
 
-		templ, err := getTemplate(path)
+		b, err := afero.ReadFile(t.Fs.Source, path)
 
 		if err != nil {
 			return err
@@ -636,33 +420,16 @@
 
 		t.Log.DEBUG.Printf("Add template file from path %s", path)
 
-		return t.AddTemplate(name, templ)
+		return t.AddTemplate(name, string(b))
 	}
 
 }
 
-func (t *templateHandler) loadEmbedded() {
-	t.embedShortcodes()
-	t.embedTemplates()
+func (t *GoHTMLTemplate) GenerateTemplateNameFrom(base, path string) string {
+	name, _ := filepath.Rel(base, path)
+	return filepath.ToSlash(name)
 }
 
-func (t *templateHandler) addInternalTemplate(prefix, name, tpl string) error {
-	if prefix != "" {
-		return t.AddTemplate("_internal/"+prefix+"/"+name, tpl)
-	}
-	return t.AddTemplate("_internal/"+name, tpl)
-}
-
-func (t *templateHandler) addInternalShortcode(name, content string) error {
-	return t.addInternalTemplate("shortcodes", name, content)
-}
-
-func (t *templateHandler) checkState() {
-	if t.html.clone != nil || t.text.clone != nil {
-		panic("template is cloned and cannot be modfified")
-	}
-}
-
 func isDotFile(path string) bool {
 	return filepath.Base(path)[0] == '.'
 }
@@ -675,4 +442,97 @@
 
 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
+			}
+
+			var (
+				workingDir = t.PathSpec.WorkingDir()
+				themeDir   = t.PathSpec.GetThemeDir()
+				layoutDir  = t.PathSpec.LayoutDir()
+			)
+
+			if themeDir != "" && strings.HasPrefix(absPath, themeDir) {
+				workingDir = themeDir
+				layoutDir = "layouts"
+			}
+
+			li := strings.LastIndex(path, layoutDir) + len(layoutDir) + 1
+			relPath := path[li:]
+
+			descriptor := output.TemplateLookupDescriptor{
+				WorkingDir: workingDir,
+				LayoutDir:  layoutDir,
+				RelPath:    relPath,
+				Prefix:     prefix,
+				Theme:      t.PathSpec.Theme(),
+				FileExists: func(filename string) (bool, error) {
+					return helpers.Exists(filename, t.Fs.Source)
+				},
+				ContainsAny: func(filename string, subslices [][]byte) (bool, error) {
+					return helpers.FileContainsAny(filename, subslices, t.Fs.Source)
+				},
+			}
+
+			tplID, err := output.CreateTemplateNames(descriptor)
+			if err != nil {
+				t.Log.ERROR.Printf("Failed to resolve template in path %q: %s", path, err)
+
+				return nil
+			}
+
+			if err := t.AddTemplateFile(tplID.Name, tplID.MasterFilename, tplID.OverlayFilename); err != nil {
+				t.Log.ERROR.Printf("Failed to add template %q in path %q: %s", tplID.Name, 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 (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)
+	}
 }
--- a/tpl/tplimpl/templateFuncster.go
+++ /dev/null
@@ -1,86 +1,0 @@
-// 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 tplimpl
-
-import (
-	"fmt"
-	"html/template"
-	"strings"
-
-	bp "github.com/spf13/hugo/bufferpool"
-
-	"image"
-
-	"github.com/spf13/hugo/deps"
-)
-
-// Some of the template funcs are'nt entirely stateless.
-type templateFuncster struct {
-	funcMap        template.FuncMap
-	cachedPartials partialCache
-	image          *imageHandler
-
-	// Make sure each funcster gets its own TemplateFinder to get
-	// proper text and HTML template separation.
-	Tmpl templateFuncsterTemplater
-
-	*deps.Deps
-}
-
-func newTemplateFuncster(deps *deps.Deps, t templateFuncsterTemplater) *templateFuncster {
-	return &templateFuncster{
-		Deps:           deps,
-		Tmpl:           t,
-		cachedPartials: partialCache{p: make(map[string]interface{})},
-		image:          &imageHandler{fs: deps.Fs, imageConfigCache: map[string]image.Config{}},
-	}
-}
-
-// Partial executes the named partial and returns either a string,
-// when called from text/template, for or a template.HTML.
-func (t *templateFuncster) partial(name string, contextList ...interface{}) (interface{}, error) {
-	if strings.HasPrefix("partials/", name) {
-		name = name[8:]
-	}
-	var context interface{}
-
-	if len(contextList) == 0 {
-		context = nil
-	} else {
-		context = contextList[0]
-	}
-
-	for _, n := range []string{"partials/" + name, "theme/partials/" + name} {
-		templ := t.Tmpl.Lookup(n)
-		if templ != nil {
-			b := bp.GetBuffer()
-			defer bp.PutBuffer(b)
-
-			if err := templ.Execute(b, context); err != nil {
-				return "", err
-			}
-
-			switch t.Tmpl.(type) {
-			case *htmlTemplates:
-				return template.HTML(b.String()), nil
-			case *textTemplates:
-				return b.String(), nil
-			default:
-				panic("Unknown type")
-			}
-		}
-	}
-
-	return "", fmt.Errorf("Partial %q not found", name)
-}
--- a/tpl/tplimpl/templateProvider.go
+++ /dev/null
@@ -1,59 +1,0 @@
-// 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 tplimpl
-
-import (
-	"github.com/spf13/hugo/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 {
-
-	newTmpl := newTemplateAdapter(deps)
-	deps.Tmpl = newTmpl
-
-	newTmpl.initFuncs()
-	newTmpl.loadEmbedded()
-
-	if deps.WithTemplate != nil {
-		err := deps.WithTemplate(newTmpl)
-		if err != nil {
-			newTmpl.addError("init", err)
-		}
-
-	}
-
-	newTmpl.MarkReady()
-
-	return nil
-
-}
-
-// Clone clones.
-func (*TemplateProvider) Clone(d *deps.Deps) error {
-
-	t := d.Tmpl.(*templateHandler)
-	clone := t.clone(d)
-
-	d.Tmpl = clone
-
-	clone.MarkReady()
-
-	return nil
-}
--- a/tpl/tplimpl/template_ast_transformers.go
+++ b/tpl/tplimpl/template_ast_transformers.go
@@ -17,7 +17,6 @@
 	"errors"
 	"html/template"
 	"strings"
-	texttemplate "text/template"
 	"text/template/parse"
 )
 
@@ -36,57 +35,32 @@
 }
 
 type templateContext struct {
-	decl     decl
-	visited  map[string]bool
-	lookupFn func(name string) *parse.Tree
+	decl    decl
+	templ   *template.Template
+	visited map[string]bool
 }
 
-func (c templateContext) getIfNotVisited(name string) *parse.Tree {
+func (c templateContext) getIfNotVisited(name string) *template.Template {
 	if c.visited[name] {
 		return nil
 	}
 	c.visited[name] = true
-	return c.lookupFn(name)
+	return c.templ.Lookup(name)
 }
 
-func newTemplateContext(lookupFn func(name string) *parse.Tree) *templateContext {
-	return &templateContext{lookupFn: lookupFn, decl: make(map[string]string), visited: make(map[string]bool)}
+func newTemplateContext(templ *template.Template) *templateContext {
+	return &templateContext{templ: templ, decl: make(map[string]string), visited: make(map[string]bool)}
 
 }
 
-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 {
+func applyTemplateTransformers(templ *template.Template) error {
+	if templ == nil || templ.Tree == nil {
 		return errors.New("expected template, but none provided")
 	}
 
-	c := newTemplateContext(lookupFn)
+	c := newTemplateContext(templ)
 
-	c.paramsKeysToLower(templ.Root)
+	c.paramsKeysToLower(templ.Tree.Root)
 
 	return nil
 }
@@ -110,7 +84,7 @@
 	case *parse.TemplateNode:
 		subTempl := c.getIfNotVisited(x.Name)
 		if subTempl != nil {
-			c.paramsKeysToLowerForNodes(subTempl.Root)
+			c.paramsKeysToLowerForNodes(subTempl.Tree.Root)
 		}
 	case *parse.PipeNode:
 		for i, elem := range x.Decl {
--- a/tpl/tplimpl/template_ast_transformers_test.go
+++ b/tpl/tplimpl/template_ast_transformers_test.go
@@ -115,13 +115,13 @@
 func TestParamsKeysToLower(t *testing.T) {
 	t.Parallel()
 
-	require.Error(t, applyTemplateTransformers(nil, nil))
+	require.Error(t, applyTemplateTransformers(nil))
 
 	templ, err := template.New("foo").Funcs(testFuncs).Parse(paramsTempl)
 
 	require.NoError(t, err)
 
-	c := newTemplateContext(createParseTreeLookup(templ))
+	c := newTemplateContext(templ)
 
 	require.Equal(t, -1, c.decl.indexOfReplacementStart([]string{}))
 
@@ -185,7 +185,7 @@
 	b.ResetTimer()
 
 	for i := 0; i < b.N; i++ {
-		c := newTemplateContext(createParseTreeLookup(templates[i]))
+		c := newTemplateContext(templates[i])
 		c.paramsKeysToLower(templ.Tree.Root)
 	}
 }
@@ -214,7 +214,7 @@
 
 	require.NoError(t, err)
 
-	c := newTemplateContext(createParseTreeLookup(templ))
+	c := newTemplateContext(templ)
 
 	c.paramsKeysToLower(templ.Tree.Root)
 
@@ -254,7 +254,7 @@
 	require.NoError(t, err)
 	overlayTpl = overlayTpl.Lookup(overlayTpl.Name())
 
-	c := newTemplateContext(createParseTreeLookup(overlayTpl))
+	c := newTemplateContext(overlayTpl)
 
 	c.paramsKeysToLower(overlayTpl.Tree.Root)
 
@@ -284,7 +284,7 @@
 	templ, err := template.New("foo").Parse(recursive)
 	require.NoError(t, err)
 
-	c := newTemplateContext(createParseTreeLookup(templ))
+	c := newTemplateContext(templ)
 	c.paramsKeysToLower(templ.Tree.Root)
 
 }
--- a/tpl/tplimpl/template_embedded.go
+++ b/tpl/tplimpl/template_embedded.go
@@ -1,4 +1,4 @@
-// Copyright 2017-present The Hugo Authors. All rights reserved.
+// Copyright 2015 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.
@@ -13,12 +13,17 @@
 
 package tplimpl
 
-func (t *templateHandler) embedShortcodes() {
-	t.addInternalShortcode("ref.html", `{{ .Get 0 | ref .Page }}`)
-	t.addInternalShortcode("relref.html", `{{ .Get 0 | relref .Page }}`)
-	t.addInternalShortcode("highlight.html", `{{ if len .Params | eq 2 }}{{ highlight .Inner (.Get 0) (.Get 1) }}{{ else }}{{ highlight .Inner (.Get 0) "" }}{{ end }}`)
-	t.addInternalShortcode("test.html", `This is a simple Test`)
-	t.addInternalShortcode("figure.html", `<!-- image -->
+type Tmpl struct {
+	Name string
+	Data string
+}
+
+func (t *GoHTMLTemplate) EmbedShortcodes() {
+	t.AddInternalShortcode("ref.html", `{{ .Get 0 | ref .Page }}`)
+	t.AddInternalShortcode("relref.html", `{{ .Get 0 | relref .Page }}`)
+	t.AddInternalShortcode("highlight.html", `{{ if len .Params | eq 2 }}{{ highlight .Inner (.Get 0) (.Get 1) }}{{ else }}{{ highlight .Inner (.Get 0) "" }}{{ end }}`)
+	t.AddInternalShortcode("test.html", `This is a simple Test`)
+	t.AddInternalShortcode("figure.html", `<!-- image -->
 <figure {{ with .Get "class" }}class="{{.}}"{{ end }}>
     {{ with .Get "link"}}<a href="{{.}}">{{ end }}
         <img src="{{ .Get "src" }}" {{ if or (.Get "alt") (.Get "caption") }}alt="{{ with .Get "alt"}}{{.}}{{else}}{{ .Get "caption" }}{{ end }}" {{ end }}{{ with .Get "width" }}width="{{.}}" {{ end }}/>
@@ -36,8 +41,8 @@
     {{ end }}
 </figure>
 <!-- image -->`)
-	t.addInternalShortcode("speakerdeck.html", "<script async class='speakerdeck-embed' data-id='{{ index .Params 0 }}' data-ratio='1.33333333333333' src='//speakerdeck.com/assets/embed.js'></script>")
-	t.addInternalShortcode("youtube.html", `{{ if .IsNamedParams }}
+	t.AddInternalShortcode("speakerdeck.html", "<script async class='speakerdeck-embed' data-id='{{ index .Params 0 }}' data-ratio='1.33333333333333' src='//speakerdeck.com/assets/embed.js'></script>")
+	t.AddInternalShortcode("youtube.html", `{{ if .IsNamedParams }}
 <div {{ if .Get "class" }}class="{{ .Get "class" }}"{{ else }}style="position: relative; padding-bottom: 56.25%; padding-top: 30px; height: 0; overflow: hidden;"{{ end }}>
   <iframe src="//www.youtube.com/embed/{{ .Get "id" }}?{{ with .Get "autoplay" }}{{ if eq . "true" }}autoplay=1{{ end }}{{ end }}" 
   {{ if not (.Get "class") }}style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;" {{ end }}allowfullscreen frameborder="0"></iframe>
@@ -46,7 +51,7 @@
   <iframe src="//www.youtube.com/embed/{{ .Get 0 }}" {{ if len .Params | eq 1 }}style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;" {{ end }}allowfullscreen frameborder="0"></iframe>
  </div>
 {{ end }}`)
-	t.addInternalShortcode("vimeo.html", `{{ if .IsNamedParams }}<div {{ if .Get "class" }}class="{{ .Get "class" }}"{{ else }}style="position: relative; padding-bottom: 56.25%; padding-top: 30px; height: 0; overflow: hidden;"{{ end }}>
+	t.AddInternalShortcode("vimeo.html", `{{ if .IsNamedParams }}<div {{ if .Get "class" }}class="{{ .Get "class" }}"{{ else }}style="position: relative; padding-bottom: 56.25%; padding-top: 30px; height: 0; overflow: hidden;"{{ end }}>
   <iframe src="//player.vimeo.com/video/{{ .Get "id" }}" {{ if not (.Get "class") }}style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;" {{ end }}webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
  </div>{{ else }}
 <div {{ if len .Params | eq 2 }}class="{{ .Get 1 }}"{{ else }}style="position: relative; padding-bottom: 56.25%; padding-top: 30px; height: 0; overflow: hidden;"{{ end }}>
@@ -53,14 +58,14 @@
   <iframe src="//player.vimeo.com/video/{{ .Get 0 }}" {{ if len .Params | eq 1 }}style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;" {{ end }}webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
  </div>
 {{ end }}`)
-	t.addInternalShortcode("gist.html", `<script src="//gist.github.com/{{ index .Params 0 }}/{{ index .Params 1 }}.js{{if len .Params | eq 3 }}?file={{ index .Params 2 }}{{end}}"></script>`)
-	t.addInternalShortcode("tweet.html", `{{ (getJSON "https://api.twitter.com/1/statuses/oembed.json?id=" (index .Params 0)).html | safeHTML }}`)
-	t.addInternalShortcode("instagram.html", `{{ if len .Params | eq 2 }}{{ if eq (.Get 1) "hidecaption" }}{{ with getJSON "https://api.instagram.com/oembed/?url=https://instagram.com/p/" (index .Params 0) "/&hidecaption=1" }}{{ .html | safeHTML }}{{ end }}{{ end }}{{ else }}{{ with getJSON "https://api.instagram.com/oembed/?url=https://instagram.com/p/" (index .Params 0) "/&hidecaption=0" }}{{ .html | safeHTML }}{{ end }}{{ end }}`)
+	t.AddInternalShortcode("gist.html", `<script src="//gist.github.com/{{ index .Params 0 }}/{{ index .Params 1 }}.js{{if len .Params | eq 3 }}?file={{ index .Params 2 }}{{end}}"></script>`)
+	t.AddInternalShortcode("tweet.html", `{{ (getJSON "https://api.twitter.com/1/statuses/oembed.json?id=" (index .Params 0)).html | safeHTML }}`)
+	t.AddInternalShortcode("instagram.html", `{{ if len .Params | eq 2 }}{{ if eq (.Get 1) "hidecaption" }}{{ with getJSON "https://api.instagram.com/oembed/?url=https://instagram.com/p/" (index .Params 0) "/&hidecaption=1" }}{{ .html | safeHTML }}{{ end }}{{ end }}{{ else }}{{ with getJSON "https://api.instagram.com/oembed/?url=https://instagram.com/p/" (index .Params 0) "/&hidecaption=0" }}{{ .html | safeHTML }}{{ end }}{{ end }}`)
 }
 
-func (t *templateHandler) embedTemplates() {
+func (t *GoHTMLTemplate) EmbedTemplates() {
 
-	t.addInternalTemplate("_default", "rss.xml", `<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
+	t.AddInternalTemplate("_default", "rss.xml", `<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
   <channel>
     <title>{{ if eq  .Title  .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{.}} on {{ end }}{{ .Site.Title }}{{ end }}</title>
     <link>{{ .Permalink }}</link>
@@ -87,7 +92,7 @@
   </channel>
 </rss>`)
 
-	t.addInternalTemplate("_default", "sitemap.xml", `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
+	t.AddInternalTemplate("_default", "sitemap.xml", `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
   {{ range .Data.Pages }}
   <url>
     <loc>{{ .Permalink }}</loc>{{ if not .Lastmod.IsZero }}
@@ -99,7 +104,7 @@
 </urlset>`)
 
 	// For multilanguage sites
-	t.addInternalTemplate("_default", "sitemapindex.xml", `<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
+	t.AddInternalTemplate("_default", "sitemapindex.xml", `<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
 	{{ range . }}
 	<sitemap>
 	   	<loc>{{ .SitemapAbsURL }}</loc>
@@ -111,7 +116,7 @@
 </sitemapindex>
 `)
 
-	t.addInternalTemplate("", "pagination.html", `{{ $pag := $.Paginator }}
+	t.AddInternalTemplate("", "pagination.html", `{{ $pag := $.Paginator }}
     {{ if gt $pag.TotalPages 1 }}
     <ul class="pagination">
         {{ with $pag.First }}
@@ -139,7 +144,7 @@
     </ul>
     {{ end }}`)
 
-	t.addInternalTemplate("", "disqus.html", `{{ if .Site.DisqusShortname }}<div id="disqus_thread"></div>
+	t.AddInternalTemplate("", "disqus.html", `{{ if .Site.DisqusShortname }}<div id="disqus_thread"></div>
 <script type="text/javascript">
     var disqus_shortname = '{{ .Site.DisqusShortname }}';
     var disqus_identifier = '{{with .GetParam "disqus_identifier" }}{{ . }}{{ else }}{{ .Permalink }}{{end}}';
@@ -156,7 +161,7 @@
 <a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>{{end}}`)
 
 	// Add SEO & Social metadata
-	t.addInternalTemplate("", "opengraph.html", `<meta property="og:title" content="{{ .Title }}" />
+	t.AddInternalTemplate("", "opengraph.html", `<meta property="og:title" content="{{ .Title }}" />
 <meta property="og:description" content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end }}" />
 <meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}" />
 <meta property="og:url" content="{{ .Permalink }}" />
@@ -200,7 +205,7 @@
 <!-- Facebook Page Admin ID for Domain Insights -->
 {{ with .Site.Social.facebook_admin }}<meta property="fb:admins" content="{{ . }}" />{{ end }}`)
 
-	t.addInternalTemplate("", "twitter_cards.html", `{{ if .IsPage }}
+	t.AddInternalTemplate("", "twitter_cards.html", `{{ if .IsPage }}
 {{ with .Params.images }}
 <!-- Twitter summary card with large image must be at least 280x150px -->
   <meta name="twitter:card" content="summary_large_image"/>
@@ -218,11 +223,11 @@
   {{ with .twitter }}<meta name="twitter:creator" content="@{{ . }}"/>{{ end }}
 {{ end }}{{ end }}`)
 
-	t.addInternalTemplate("", "google_news.html", `{{ if .IsPage }}{{ with .Params.news_keywords }}
+	t.AddInternalTemplate("", "google_news.html", `{{ if .IsPage }}{{ with .Params.news_keywords }}
   <meta name="news_keywords" content="{{ range $i, $kw := first 10 . }}{{ if $i }},{{ end }}{{ $kw }}{{ end }}" />
 {{ end }}{{ end }}`)
 
-	t.addInternalTemplate("", "schema.html", `{{ with .Site.Social.GooglePlus }}<link rel="publisher" href="{{ . }}"/>{{ end }}
+	t.AddInternalTemplate("", "schema.html", `{{ with .Site.Social.GooglePlus }}<link rel="publisher" href="{{ . }}"/>{{ end }}
 <meta itemprop="name" content="{{ .Title }}">
 <meta itemprop="description" content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end }}">
 
@@ -238,7 +243,7 @@
 <meta itemprop="keywords" content="{{ range $plural, $terms := .Site.Taxonomies }}{{ range $term, $val := $terms }}{{ printf "%s," $term }}{{ end }}{{ end }}" />
 {{ end }}`)
 
-	t.addInternalTemplate("", "google_analytics.html", `{{ with .Site.GoogleAnalytics }}
+	t.AddInternalTemplate("", "google_analytics.html", `{{ with .Site.GoogleAnalytics }}
 <script>
 (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
 (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
@@ -250,7 +255,7 @@
 </script>
 {{ end }}`)
 
-	t.addInternalTemplate("", "google_analytics_async.html", `{{ with .Site.GoogleAnalytics }}
+	t.AddInternalTemplate("", "google_analytics_async.html", `{{ with .Site.GoogleAnalytics }}
 <script>
 window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
 ga('create', '{{ . }}', 'auto');
@@ -259,5 +264,5 @@
 <script async src='//www.google-analytics.com/analytics.js'></script>
 {{ end }}`)
 
-	t.addInternalTemplate("_default", "robots.txt", "User-agent: *")
+	t.AddInternalTemplate("_default", "robots.txt", "User-agent: *")
 }
--- a/tpl/tplimpl/template_funcs.go
+++ b/tpl/tplimpl/template_funcs.go
@@ -45,6 +45,7 @@
 	"github.com/bep/inflect"
 	"github.com/spf13/afero"
 	"github.com/spf13/cast"
+	"github.com/spf13/hugo/deps"
 	"github.com/spf13/hugo/helpers"
 	jww "github.com/spf13/jwalterweatherman"
 
@@ -54,6 +55,22 @@
 	_ "image/png"
 )
 
+// Some of the template funcs are'nt entirely stateless.
+type templateFuncster struct {
+	funcMap        template.FuncMap
+	cachedPartials partialCache
+	image          *imageHandler
+	*deps.Deps
+}
+
+func newTemplateFuncster(deps *deps.Deps) *templateFuncster {
+	return &templateFuncster{
+		Deps:           deps,
+		cachedPartials: partialCache{p: make(map[string]template.HTML)},
+		image:          &imageHandler{fs: deps.Fs, imageConfigCache: map[string]image.Config{}},
+	}
+}
+
 // eq returns the boolean truth of arg1 == arg2.
 func eq(x, y interface{}) bool {
 	normalize := func(v interface{}) interface{} {
@@ -1541,13 +1558,13 @@
 // partialCache represents a cache of partials protected by a mutex.
 type partialCache struct {
 	sync.RWMutex
-	p map[string]interface{}
+	p map[string]template.HTML
 }
 
 // Get retrieves partial output from the cache based upon the partial name.
 // If the partial is not found in the cache, the partial is rendered and added
 // to the cache.
-func (t *templateFuncster) Get(key, name string, context interface{}) (p interface{}, err error) {
+func (t *templateFuncster) Get(key, name string, context interface{}) (p template.HTML) {
 	var ok bool
 
 	t.cachedPartials.RLock()
@@ -1555,13 +1572,13 @@
 	t.cachedPartials.RUnlock()
 
 	if ok {
-		return
+		return p
 	}
 
 	t.cachedPartials.Lock()
 	if p, ok = t.cachedPartials.p[key]; !ok {
 		t.cachedPartials.Unlock()
-		p, err = t.partial(name, context)
+		p = t.Tmpl.Partial(name, context)
 
 		t.cachedPartials.Lock()
 		t.cachedPartials.p[key] = p
@@ -1569,7 +1586,7 @@
 	}
 	t.cachedPartials.Unlock()
 
-	return
+	return p
 }
 
 // partialCached executes and caches partial templates.  An optional variant
@@ -1576,7 +1593,7 @@
 // string parameter (a string slice actually, but be only use a variadic
 // argument to make it optional) can be passed so that a given partial can have
 // multiple uses.  The cache is created with name+variant as the key.
-func (t *templateFuncster) partialCached(name string, context interface{}, variant ...string) (interface{}, error) {
+func (t *templateFuncster) partialCached(name string, context interface{}, variant ...string) template.HTML {
 	key := name
 	if len(variant) > 0 {
 		for i := 0; i < len(variant); i++ {
@@ -2178,7 +2195,7 @@
 		"mul":           func(a, b interface{}) (interface{}, error) { return helpers.DoArithmetic(a, b, '*') },
 		"ne":            ne,
 		"now":           func() time.Time { return time.Now() },
-		"partial":       t.partial,
+		"partial":       t.Tmpl.Partial,
 		"partialCached": t.partialCached,
 		"plainify":      plainify,
 		"pluralize":     pluralize,
@@ -2232,5 +2249,5 @@
 	}
 
 	t.funcMap = funcMap
-	t.Tmpl.setFuncs(funcMap)
+	t.Tmpl.Funcs(funcMap)
 }
--- a/tpl/tplimpl/template_funcs_test.go
+++ b/tpl/tplimpl/template_funcs_test.go
@@ -281,8 +281,8 @@
 	v.Set("CurrentContentLanguage", helpers.NewLanguage("en", v))
 
 	config := newDepsConfig(v)
-	config.WithTemplate = func(templ tpl.TemplateHandler) error {
-		if err := templ.AddTemplate("test", in); err != nil {
+	config.WithTemplate = func(templ tpl.Template) error {
+		if _, err := templ.New("test").Parse(in); err != nil {
 			t.Fatal("Got error on parse", err)
 		}
 		return nil
@@ -2858,96 +2858,6 @@
 	}
 }
 
-func TestPartialHTMLAndText(t *testing.T) {
-	t.Parallel()
-	config := newDepsConfig(viper.New())
-
-	data := struct {
-		Name string
-	}{
-		Name: "a+b+c", // This should get encoded in HTML.
-	}
-
-	config.WithTemplate = func(templ tpl.TemplateHandler) error {
-		if err := templ.AddTemplate("htmlTemplate.html", `HTML Test Partial: {{ partial "test.foo" . -}}`); err != nil {
-			return err
-		}
-
-		if err := templ.AddTemplate("_text/textTemplate.txt", `Text Test Partial: {{ partial "test.foo" . -}}`); err != nil {
-			return err
-		}
-
-		// Use "foo" here to say that the extension doesn't really matter in this scenario.
-		// It will look for templates in "partials/test.foo" and "partials/test.foo.html".
-		if err := templ.AddTemplate("partials/test.foo", "HTML Name: {{ .Name }}"); err != nil {
-			return err
-		}
-		if err := templ.AddTemplate("_text/partials/test.foo", "Text Name: {{ .Name }}"); err != nil {
-			return err
-		}
-
-		return nil
-	}
-
-	de, err := deps.New(config)
-	require.NoError(t, err)
-	require.NoError(t, de.LoadResources())
-
-	templ := de.Tmpl.Lookup("htmlTemplate.html")
-	require.NotNil(t, templ)
-	resultHTML, err := templ.ExecuteToString(data)
-	require.NoError(t, err)
-
-	templ = de.Tmpl.Lookup("_text/textTemplate.txt")
-	require.NotNil(t, templ)
-	resultText, err := templ.ExecuteToString(data)
-	require.NoError(t, err)
-
-	require.Contains(t, resultHTML, "HTML Test Partial: HTML Name: a&#43;b&#43;c")
-	require.Contains(t, resultText, "Text Test Partial: Text Name: a+b+c")
-
-}
-
-func TestPartialWithError(t *testing.T) {
-	t.Parallel()
-	config := newDepsConfig(viper.New())
-
-	data := struct {
-		Name string
-	}{
-		Name: "bep",
-	}
-
-	config.WithTemplate = func(templ tpl.TemplateHandler) error {
-		if err := templ.AddTemplate("container.html", `HTML Test Partial: {{ partial "fail.foo" . -}}`); err != nil {
-			return err
-		}
-
-		if err := templ.AddTemplate("partials/fail.foo", "Template: {{ .DoesNotExist }}"); err != nil {
-			return err
-		}
-
-		return nil
-	}
-
-	de, err := deps.New(config)
-	require.NoError(t, err)
-	require.NoError(t, de.LoadResources())
-
-	templ := de.Tmpl.Lookup("container.html")
-	require.NotNil(t, templ)
-	result, err := templ.ExecuteToString(data)
-	require.Error(t, err)
-
-	errStr := err.Error()
-
-	require.Contains(t, errStr, `template: container.html:1:22: executing "container.html" at <partial "fail.foo" .>`)
-	require.Contains(t, errStr, `can't evaluate field DoesNotExist`)
-
-	require.Empty(t, result)
-
-}
-
 func TestPartialCached(t *testing.T) {
 	t.Parallel()
 	testCases := []struct {
@@ -2983,7 +2893,7 @@
 
 		config := newDepsConfig(viper.New())
 
-		config.WithTemplate = func(templ tpl.TemplateHandler) error {
+		config.WithTemplate = func(templ tpl.Template) error {
 			err := templ.AddTemplate("testroot", tmp)
 			if err != nil {
 				return err
@@ -3023,7 +2933,7 @@
 
 func BenchmarkPartial(b *testing.B) {
 	config := newDepsConfig(viper.New())
-	config.WithTemplate = func(templ tpl.TemplateHandler) error {
+	config.WithTemplate = func(templ tpl.Template) error {
 		err := templ.AddTemplate("testroot", `{{ partial "bench1" . }}`)
 		if err != nil {
 			return err
@@ -3055,7 +2965,7 @@
 
 func BenchmarkPartialCached(b *testing.B) {
 	config := newDepsConfig(viper.New())
-	config.WithTemplate = func(templ tpl.TemplateHandler) error {
+	config.WithTemplate = func(templ tpl.Template) error {
 		err := templ.AddTemplate("testroot", `{{ partialCached "bench1" . }}`)
 		if err != nil {
 			return err
@@ -3100,12 +3010,12 @@
 		panic(err)
 	}
 
-	return d.Tmpl.(*templateHandler).html.funcster
+	return d.Tmpl.(*GoHTMLTemplate).funcster
 }
 
-func newTestTemplate(t *testing.T, name, template string) tpl.Template {
+func newTestTemplate(t *testing.T, name, template string) *template.Template {
 	config := newDepsConfig(viper.New())
-	config.WithTemplate = func(templ tpl.TemplateHandler) error {
+	config.WithTemplate = func(templ tpl.Template) error {
 		err := templ.AddTemplate(name, template)
 		if err != nil {
 			return err
--- a/tpl/tplimpl/template_test.go
+++ b/tpl/tplimpl/template_test.go
@@ -14,10 +14,17 @@
 package tplimpl
 
 import (
+	"bytes"
 	"errors"
+	"html/template"
 	"io/ioutil"
+	"os"
+	"path/filepath"
+	"runtime"
+	"strings"
 	"testing"
 
+	"github.com/spf13/afero"
 	"github.com/spf13/hugo/deps"
 
 	"github.com/spf13/hugo/tpl"
@@ -25,6 +32,223 @@
 	"github.com/stretchr/testify/require"
 )
 
+// Some tests for Issue #1178 -- Ace
+func TestAceTemplates(t *testing.T) {
+	t.Parallel()
+
+	for i, this := range []struct {
+		basePath     string
+		innerPath    string
+		baseContent  string
+		innerContent string
+		expect       string
+		expectErr    int
+	}{
+		{"", filepath.FromSlash("_default/single.ace"), "", "{{ . }}", "DATA", 0},
+		{filepath.FromSlash("_default/baseof.ace"), filepath.FromSlash("_default/single.ace"),
+			`= content main
+  h2 This is a content named "main" of an inner template. {{ . }}`,
+			`= doctype html
+html lang=en
+  head
+    meta charset=utf-8
+    title Base and Inner Template
+  body
+    h1 This is a base template {{ . }}
+    = yield main`, `<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>Base and Inner Template</title></head><body><h1>This is a base template DATA</h1></body></html>`, 0},
+	} {
+
+		for _, root := range []string{"", os.TempDir()} {
+
+			basePath := this.basePath
+			innerPath := this.innerPath
+
+			if basePath != "" && root != "" {
+				basePath = filepath.Join(root, basePath)
+			}
+
+			if innerPath != "" && root != "" {
+				innerPath = filepath.Join(root, innerPath)
+			}
+
+			d := "DATA"
+
+			config := newDepsConfig(viper.New())
+			config.WithTemplate = func(templ tpl.Template) error {
+				return templ.AddAceTemplate("mytemplate.ace", basePath, innerPath,
+					[]byte(this.baseContent), []byte(this.innerContent))
+			}
+
+			a, err := deps.New(config)
+			require.NoError(t, err)
+
+			if err := a.LoadResources(); err != nil {
+				t.Fatal(err)
+			}
+
+			templ := a.Tmpl.(*GoHTMLTemplate)
+
+			if len(templ.errors) > 0 && this.expectErr == 0 {
+				t.Errorf("Test %d with root '%s' errored: %v", i, root, templ.errors)
+			} else if len(templ.errors) == 0 && this.expectErr == 1 {
+				t.Errorf("#1 Test %d with root '%s' should have errored", i, root)
+			}
+
+			var buff bytes.Buffer
+			err = a.Tmpl.ExecuteTemplate(&buff, "mytemplate.html", d)
+
+			if err != nil && this.expectErr == 0 {
+				t.Errorf("Test %d with root '%s' errored: %s", i, root, err)
+			} else if err == nil && this.expectErr == 2 {
+				t.Errorf("#2 Test with root '%s' %d should have errored", root, i)
+			} else {
+				result := buff.String()
+				if result != this.expect {
+					t.Errorf("Test %d  with root '%s' got\n%s\nexpected\n%s", i, root, result, this.expect)
+				}
+			}
+
+		}
+	}
+
+}
+
+func isAtLeastGo16() bool {
+	version := runtime.Version()
+	return strings.Contains(version, "1.6") || strings.Contains(version, "1.7")
+}
+
+func TestAddTemplateFileWithMaster(t *testing.T) {
+	t.Parallel()
+
+	if !isAtLeastGo16() {
+		t.Skip("This test only runs on Go >= 1.6")
+	}
+
+	for i, this := range []struct {
+		masterTplContent  string
+		overlayTplContent string
+		writeSkipper      int
+		expect            interface{}
+	}{
+		{`A{{block "main" .}}C{{end}}C`, `{{define "main"}}B{{end}}`, 0, "ABC"},
+		{`A{{block "main" .}}C{{end}}C{{block "sub" .}}D{{end}}E`, `{{define "main"}}B{{end}}`, 0, "ABCDE"},
+		{`A{{block "main" .}}C{{end}}C{{block "sub" .}}D{{end}}E`, `{{define "main"}}B{{end}}{{define "sub"}}Z{{end}}`, 0, "ABCZE"},
+		{`tpl`, `tpl`, 1, false},
+		{`tpl`, `tpl`, 2, false},
+		{`{{.0.E}}`, `tpl`, 0, false},
+		{`tpl`, `{{.0.E}}`, 0, false},
+	} {
+
+		overlayTplName := "ot"
+		masterTplName := "mt"
+		finalTplName := "tp"
+
+		config := newDepsConfig(viper.New())
+		config.WithTemplate = func(templ tpl.Template) error {
+
+			err := templ.AddTemplateFileWithMaster(finalTplName, overlayTplName, masterTplName)
+
+			if b, ok := this.expect.(bool); ok && !b {
+				if err == nil {
+					t.Errorf("[%d] AddTemplateFileWithMaster didn't return an expected error", i)
+				}
+			} else {
+
+				if err != nil {
+					t.Errorf("[%d] AddTemplateFileWithMaster failed: %s", i, err)
+					return nil
+				}
+
+				resultTpl := templ.Lookup(finalTplName)
+
+				if resultTpl == nil {
+					t.Errorf("[%d] AddTemplateFileWithMaster: Result template not found", i)
+					return nil
+				}
+
+				var b bytes.Buffer
+				err := resultTpl.Execute(&b, nil)
+
+				if err != nil {
+					t.Errorf("[%d] AddTemplateFileWithMaster execute failed: %s", i, err)
+					return nil
+				}
+				resultContent := b.String()
+
+				if resultContent != this.expect {
+					t.Errorf("[%d] AddTemplateFileWithMaster got \n%s but expected \n%v", i, resultContent, this.expect)
+				}
+			}
+
+			return nil
+		}
+
+		if this.writeSkipper != 1 {
+			afero.WriteFile(config.Fs.Source, masterTplName, []byte(this.masterTplContent), 0644)
+		}
+		if this.writeSkipper != 2 {
+			afero.WriteFile(config.Fs.Source, overlayTplName, []byte(this.overlayTplContent), 0644)
+		}
+
+		deps.New(config)
+
+	}
+
+}
+
+// A Go stdlib test for linux/arm. Will remove later.
+// See #1771
+func TestBigIntegerFunc(t *testing.T) {
+	t.Parallel()
+	var func1 = func(v int64) error {
+		return nil
+	}
+	var funcs = map[string]interface{}{
+		"A": func1,
+	}
+
+	tpl, err := template.New("foo").Funcs(funcs).Parse("{{ A 3e80 }}")
+	if err != nil {
+		t.Fatal("Parse failed:", err)
+	}
+	err = tpl.Execute(ioutil.Discard, "foo")
+
+	if err == nil {
+		t.Fatal("Execute should have failed")
+	}
+
+	t.Log("Got expected error:", err)
+
+}
+
+// A Go stdlib test for linux/arm. Will remove later.
+// See #1771
+type BI struct {
+}
+
+func (b BI) A(v int64) error {
+	return nil
+}
+func TestBigIntegerMethod(t *testing.T) {
+	t.Parallel()
+
+	data := &BI{}
+
+	tpl, err := template.New("foo2").Parse("{{ .A 3e80 }}")
+	if err != nil {
+		t.Fatal("Parse failed:", err)
+	}
+	err = tpl.ExecuteTemplate(ioutil.Discard, "foo2", data)
+
+	if err == nil {
+		t.Fatal("Execute should have failed")
+	}
+
+	t.Log("Got expected error:", err)
+
+}
+
 // Test for bugs discovered by https://github.com/dvyukov/go-fuzz
 func TestTplGoFuzzReports(t *testing.T) {
 	t.Parallel()
@@ -61,7 +285,7 @@
 
 		config := newDepsConfig(viper.New())
 
-		config.WithTemplate = func(templ tpl.TemplateHandler) error {
+		config.WithTemplate = func(templ tpl.Template) error {
 			return templ.AddTemplate("fuzz", this.data)
 		}
 
@@ -69,7 +293,7 @@
 		require.NoError(t, err)
 		require.NoError(t, de.LoadResources())
 
-		templ := de.Tmpl.(*templateHandler)
+		templ := de.Tmpl.(*GoHTMLTemplate)
 
 		if len(templ.errors) > 0 && this.expectErr == 0 {
 			t.Errorf("Test %d errored: %v", i, templ.errors)
@@ -77,9 +301,7 @@
 			t.Errorf("#1 Test %d should have errored", i)
 		}
 
-		tt := de.Tmpl.Lookup("fuzz")
-		require.NotNil(t, tt)
-		err = tt.Execute(ioutil.Discard, d)
+		err = de.Tmpl.ExecuteTemplate(ioutil.Discard, "fuzz", d)
 
 		if err != nil && this.expectErr == 0 {
 			t.Fatalf("Test %d errored: %s", i, err)