frontend/assets.go (view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | package frontend
import (
"fmt"
"hash/fnv"
"io"
"io/fs"
"path/filepath"
"github.com/Southclaws/fault"
"github.com/Southclaws/fault/fmsg"
)
type Asset struct {
StaticURL string
URL string
ETag string
Filename string
}
type AssetCollection struct {
Scripts []*Asset
Stylesheets []*Asset
ByPath map[string]*Asset
}
func New() (*AssetCollection, error) {
a := &AssetCollection{
Scripts: []*Asset{},
Stylesheets: []*Asset{},
ByPath: make(map[string]*Asset),
}
err := a.Rehash()
if err != nil {
return nil, err
}
return a, nil
}
func newAsset(filename string) (*Asset, error) {
file, err := Files.Open(filename)
if err != nil {
return nil, fault.Wrap(err, fmsg.Withf("could not open file %s", filename))
}
defer file.Close()
hash := fnv.New64a()
if _, err := io.Copy(hash, file); err != nil {
return nil, fault.Wrap(err, fmsg.Withf("could not hash file %s", filename))
}
rel, err := filepath.Rel("static", filename)
if err != nil {
return nil, fault.Wrap(err, fmsg.Withf("could not get relative path for %s", filename))
}
return &Asset{
StaticURL: "/" + filename,
URL: "/" + rel,
ETag: fmt.Sprintf(`W/"%x"`, hash.Sum(nil)),
Filename: filename,
}, nil
}
func (a *AssetCollection) Rehash() (err error) {
a.Scripts = []*Asset{}
a.Stylesheets = []*Asset{}
files, err := fs.Glob(Files, "static/**")
if err != nil {
return fault.Wrap(err, fmsg.With("could not glob files"))
}
for _, filename := range files {
asset, err := newAsset(filename)
if err != nil {
return err
}
switch filepath.Ext(filename) {
case ".js":
a.Scripts = append(a.Scripts, asset)
case ".css":
a.Stylesheets = append(a.Stylesheets, asset)
}
a.ByPath[asset.URL] = asset
a.ByPath[asset.StaticURL] = asset
}
return nil
}
|