diff --git a/README.md b/README.md index abc210e..a003d43 100644 --- a/README.md +++ b/README.md @@ -171,7 +171,27 @@ admin/edit home ~~~ -You can also load templates from memory by providing the Asset and AssetNames options, +Templates can be loaded from an `embed.FS`. + +~~~ go +// ... + +//go:embed templates/*.html templates/*.tmpl +var embeddedTemplates embed.FS + +// ... + +r := render.New(render.Options{ + Directory: "templates", + FileSystem: &EmbedFileSystem{ + FS: embeddedTemplates, + }, + Extensions: []string{".html", ".tmpl"}, +}) +// ... +~~~ + +You can also load templates from memory by providing the `Asset` and `AssetNames` options, e.g. when generating an asset file using [go-bindata](https://github.com/jteeuwen/go-bindata). ### Layouts diff --git a/fs_embed.go b/fs_embed.go new file mode 100644 index 0000000..cf9fae7 --- /dev/null +++ b/fs_embed.go @@ -0,0 +1,31 @@ +// +build go1.16 + +package render + +import ( + "embed" + "io/fs" + "path/filepath" +) + +// EmbedFileSystem implements FileSystem on top of an embed.FS +type EmbedFileSystem struct { + embed.FS +} + +var _ FileSystem = &EmbedFileSystem{} + +func (e *EmbedFileSystem) Walk(root string, walkFn filepath.WalkFunc) error { + return fs.WalkDir(e.FS, root, func(path string, d fs.DirEntry, err error) error { + if d == nil { + return nil + } + + info, err := d.Info() + if err != nil { + return err + } + + return walkFn(path, info, err) + }) +} diff --git a/fs_embed_test.go b/fs_embed_test.go new file mode 100644 index 0000000..3729327 --- /dev/null +++ b/fs_embed_test.go @@ -0,0 +1,57 @@ +// +build go1.16 + +package render + +import ( + "embed" + "net/http" + "net/http/httptest" + "testing" +) + +//go:embed fixtures/*/*.html fixtures/*/*.tmpl fixtures/*/*/*.tmpl fixtures/*/*.amber fixtures/*/*/*.amber +var EmbedFixtures embed.FS + +func TestEmbedFileSystemTemplateLookup(t *testing.T) { + baseDir := "fixtures/template-dir-test" + fname0Rel := "0" + fname1Rel := "subdir/1" + fnameShouldParsedRel := "dedicated.tmpl/notbad" + dirShouldNotParsedRel := "dedicated" + + r := New(Options{ + Directory: baseDir, + Extensions: []string{".tmpl", ".html"}, + FileSystem: &EmbedFileSystem{ + FS: EmbedFixtures, + }, + }) + + expect(t, r.TemplateLookup(fname1Rel) != nil, true) + expect(t, r.TemplateLookup(fname0Rel) != nil, true) + expect(t, r.TemplateLookup(fnameShouldParsedRel) != nil, true) + expect(t, r.TemplateLookup(dirShouldNotParsedRel) == nil, true) +} + +func TestEmbedFileSystemHTMLBasic(t *testing.T) { + render := New(Options{ + Directory: "fixtures/basic", + FileSystem: &EmbedFileSystem{ + FS: EmbedFixtures, + }, + }) + + var err error + h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + err = render.HTML(w, http.StatusOK, "hello", "gophers") + }) + + res := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/foo", nil) + h.ServeHTTP(res, req) + + expectNil(t, err) + expect(t, res.Code, 200) + expect(t, res.Header().Get(ContentType), ContentHTML+"; charset=UTF-8") + expect(t, res.Body.String(), "