diff --git a/README.md b/README.md index 2b16ff2..d5f98bc 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ import ( func main() { i, err := inertia.New(rootHTMLString) // i, err := inertia.NewFromFile("resources/views/root.html") + // i, err := inertia.NewFromFileFS(embedFS, "resources/views/root.html") // i, err := inertia.NewFromReader(rootHTMLReader) // i, err := inertia.NewFromBytes(rootHTMLBytes) if err != nil { @@ -117,6 +118,7 @@ i, err := inertia.New( /* ... */ inertia.WithVersion("some-version"), // by any string inertia.WithVersionFromFile("./public/build/manifest.json"), // by file checksum + inertia.WithVersionFromFileFS(embedFS, "./public/build/manifest.json"), // by file checksum from fs.FS ) ``` diff --git a/inertia.go b/inertia.go index 2237ee8..92b7523 100644 --- a/inertia.go +++ b/inertia.go @@ -5,6 +5,7 @@ import ( "fmt" "html/template" "io" + "io/fs" "log" "net/http" "os" @@ -56,6 +57,16 @@ func New(rootTemplateHTML string, opts ...Option) (*Inertia, error) { return i, nil } +// NewFromFileFS reads all bytes from the root template file and then initializes Inertia. +func NewFromFileFS(rootFS fs.FS, rootTemplatePath string, opts ...Option) (*Inertia, error) { + bs, err := fs.ReadFile(rootFS, rootTemplatePath) + if err != nil { + return nil, fmt.Errorf("read file %q: %w", rootTemplatePath, err) + } + + return NewFromBytes(bs, opts...) +} + // NewFromFile reads all bytes from the root template file and then initializes Inertia. func NewFromFile(rootTemplatePath string, opts ...Option) (*Inertia, error) { bs, err := os.ReadFile(rootTemplatePath) diff --git a/inertia_test.go b/inertia_test.go index 599ac57..bd21138 100644 --- a/inertia_test.go +++ b/inertia_test.go @@ -5,6 +5,7 @@ import ( "reflect" "strings" "testing" + "testing/fstest" ) var rootTemplate = ` @@ -53,6 +54,25 @@ func TestNewFromFile(t *testing.T) { } } +func TestNewFromFileFS(t *testing.T) { + t.Parallel() + + testFS := fstest.MapFS{ + "root.html": { + Data: []byte(rootTemplate), + }, + } + + i, err := NewFromFileFS(testFS, "root.html") + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if i.rootTemplateHTML != rootTemplate { + t.Fatalf("root template html=%s, want=%s", i.rootTemplateHTML, rootTemplate) + } +} + func TestNewFromReader(t *testing.T) { t.Parallel() diff --git a/option.go b/option.go index 9935dde..d8c0f9a 100644 --- a/option.go +++ b/option.go @@ -3,6 +3,7 @@ package gonertia import ( "fmt" "io" + "io/fs" "log" "net/http" ) @@ -18,6 +19,18 @@ func WithVersion(version string) Option { } } +// WithVersionFromFileFS returns Option that will set Inertia's version based on file checksum from rootFS. +func WithVersionFromFileFS(rootFS fs.FS, path string) Option { + return func(i *Inertia) (err error) { + i.version, err = md5FileFromFS(rootFS, path) + if err != nil { + return fmt.Errorf("calculating md5 hash of manifest file: %w", err) + } + + return nil + } +} + // WithVersionFromFile returns Option that will set Inertia's version based on file checksum. func WithVersionFromFile(path string) Option { return func(i *Inertia) (err error) { diff --git a/option_test.go b/option_test.go index 8be407c..1bc6f8b 100644 --- a/option_test.go +++ b/option_test.go @@ -5,6 +5,7 @@ import ( "log" "reflect" "testing" + "testing/fstest" ) func TestWithVersion(t *testing.T) { @@ -45,6 +46,30 @@ func TestWithVersionFromFile(t *testing.T) { } } +func TestWithVersionFromFileFS(t *testing.T) { + t.Parallel() + + i := I() + + mapFS := fstest.MapFS{ + "foo": { + Data: []byte("foo"), + }, + } + + option := WithVersionFromFileFS(mapFS, "foo") + + want := "acbd18db4cc2f85cedef654fccc4a4d8" + + if err := option(i); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if i.version != want { + t.Fatalf("version=%s, want=%s", i.version, want) + } +} + func TestWithJSONMarshaller(t *testing.T) { t.Parallel() diff --git a/utils.go b/utils.go index 43e90a2..43874c5 100644 --- a/utils.go +++ b/utils.go @@ -4,6 +4,7 @@ import ( crypto "crypto/md5" "encoding/hex" "io" + "io/fs" "os" ) @@ -32,18 +33,31 @@ func md5(str string) string { return hex.EncodeToString(hash[:]) } -func md5File(path string) (string, error) { +func md5FileFromFileFS(file fs.File) (string, error) { hash := crypto.New() + if _, err := io.Copy(hash, file); err != nil { + return "", err + } - f, err := os.Open(path) + return hex.EncodeToString(hash.Sum(nil)), nil +} + +func md5FileFromFS(fs fs.FS, path string) (string, error) { + f, err := fs.Open(path) if err != nil { return "", err } defer f.Close() - if _, err = io.Copy(hash, f); err != nil { + return md5FileFromFileFS(f) +} + +func md5File(path string) (string, error) { + f, err := os.Open(path) + if err != nil { return "", err } + defer f.Close() - return hex.EncodeToString(hash.Sum(nil)), nil + return md5FileFromFileFS(f) } diff --git a/utils_test.go b/utils_test.go index 4a775be..14254dd 100644 --- a/utils_test.go +++ b/utils_test.go @@ -3,6 +3,7 @@ package gonertia import ( "reflect" "testing" + "testing/fstest" ) func Test_setOf(t *testing.T) { @@ -157,3 +158,22 @@ func Test_md5File(t *testing.T) { t.Fatalf("md5File()=%s, want=%s", got, want) } } + +func Test_md5FileFromFS(t *testing.T) { + t.Parallel() + + testFS := fstest.MapFS{ + "foo": { + Data: []byte("foo"), + }, + } + + got, err := md5FileFromFS(testFS, "foo") + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if want := md5("foo"); got != want { + t.Fatalf("md5FileFromFS()=%s, want=%s", got, want) + } +}