From 3d28e5740216fd3340098880b50c755f3a1b63ed Mon Sep 17 00:00:00 2001 From: youdie006 Date: Mon, 7 Sep 2026 11:39:37 +0900 Subject: [PATCH] Select asset templates by the final extension only compileTemplatesFromDir takes filepath.Ext(rel), with a comment naming the case it fixes: a directory called users.tmpl whose files are still normal templates. compileTemplatesFromAsset never got that fix and still joins everything after the first dot, so for users.tmpl/index.tmpl it computes .tmpl/index.tmpl. No configured extension contains a path separator, so the match below is vacuously false and the template is silently skipped. The same happens for any multi-dot name such as a.b.tmpl. Use filepath.Ext here too, so both loaders select the same files and produce the same template names. --- render.go | 2 +- render_html_test.go | 30 +++++++++++++++++++++++++++ testdata/dotdir/a.b.tmpl | 1 + testdata/dotdir/users.tmpl/index.tmpl | 1 + 4 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 testdata/dotdir/a.b.tmpl create mode 100644 testdata/dotdir/users.tmpl/index.tmpl diff --git a/render.go b/render.go index 3bf5d36..ec9dbb7 100644 --- a/render.go +++ b/render.go @@ -360,7 +360,7 @@ func (r *Render) compileTemplatesFromAsset() { ext := "" if strings.Contains(rel, ".") { - ext = "." + strings.Join(strings.Split(rel, ".")[1:], ".") + ext = filepath.Ext(rel) // match the directory walker: only the final extension } for _, extension := range r.opt.Extensions { diff --git a/render_html_test.go b/render_html_test.go index 4ce17f0..08e167e 100644 --- a/render_html_test.go +++ b/render_html_test.go @@ -524,3 +524,33 @@ func TestHTMLPerCallContentType(t *testing.T) { expect(t, res.Header().Get(ContentType), "application/xhtml+xml") expect(t, res.Body.String(), "

Hello gophers

\n") } + +var dotDirFiles = map[string]string{ + "testdata/dotdir/users.tmpl/index.tmpl": "Hello asset", + "testdata/dotdir/a.b.tmpl": "Hello ab", +} + +// The asset loader must select templates by the same extension rule as the +// directory walker, including under a dot-named directory and for multi-dot names. +func TestHTMLAssetExtensionMatchesDirectory(t *testing.T) { + names := make([]string, 0, len(dotDirFiles)) + for n := range dotDirFiles { + names = append(names, n) + } + + dirRender := New(Options{Directory: "testdata/dotdir"}) + assetRender := New(Options{ + Directory: "testdata/dotdir", + Asset: func(n string) ([]byte, error) { return []byte(dotDirFiles[n]), nil }, + AssetNames: func() []string { return names }, + }) + + for _, name := range []string{"users.tmpl/index", "a.b"} { + if dirRender.TemplateLookup(name) == nil { + t.Errorf("directory mode did not load %q", name) + } + if assetRender.TemplateLookup(name) == nil { + t.Errorf("asset mode did not load %q", name) + } + } +} diff --git a/testdata/dotdir/a.b.tmpl b/testdata/dotdir/a.b.tmpl new file mode 100644 index 0000000..7fcc02d --- /dev/null +++ b/testdata/dotdir/a.b.tmpl @@ -0,0 +1 @@ +Hello ab \ No newline at end of file diff --git a/testdata/dotdir/users.tmpl/index.tmpl b/testdata/dotdir/users.tmpl/index.tmpl new file mode 100644 index 0000000..4d0ae53 --- /dev/null +++ b/testdata/dotdir/users.tmpl/index.tmpl @@ -0,0 +1 @@ +Hello asset \ No newline at end of file