mirror of
https://github.com/Xevion/glance.git
synced 2025-12-16 06:12:02 -06:00
153 lines
3.5 KiB
Go
153 lines
3.5 KiB
Go
package glance
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/md5"
|
|
"embed"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"io/fs"
|
|
"log"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
//go:embed static
|
|
var _staticFS embed.FS
|
|
|
|
//go:embed templates
|
|
var _templateFS embed.FS
|
|
|
|
var staticFS, _ = fs.Sub(_staticFS, "static")
|
|
var templateFS, _ = fs.Sub(_templateFS, "templates")
|
|
|
|
func readAllFromStaticFS(path string) ([]byte, error) {
|
|
// For some reason fs.FS only works with forward slashes, so in case we're
|
|
// running on Windows or pass paths with backslashes we need to replace them.
|
|
path = strings.ReplaceAll(path, "\\", "/")
|
|
|
|
file, err := staticFS.Open(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return io.ReadAll(file)
|
|
}
|
|
|
|
var staticFSHash = func() string {
|
|
hash, err := computeFSHash(staticFS)
|
|
if err != nil {
|
|
log.Printf("Could not compute static assets cache key: %v", err)
|
|
return strconv.FormatInt(time.Now().Unix(), 10)
|
|
}
|
|
|
|
return hash
|
|
}()
|
|
|
|
func computeFSHash(files fs.FS) (string, error) {
|
|
hash := md5.New()
|
|
|
|
err := fs.WalkDir(files, ".", func(path string, d fs.DirEntry, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if d.IsDir() {
|
|
return nil
|
|
}
|
|
|
|
file, err := files.Open(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, err := io.Copy(hash, file); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return hex.EncodeToString(hash.Sum(nil))[:10], nil
|
|
}
|
|
|
|
var cssImportPattern = regexp.MustCompile(`(?m)^@import "(.*?)";$`)
|
|
var cssSingleLineCommentPattern = regexp.MustCompile(`(?m)^\s*\/\*.*?\*\/$`)
|
|
var whitespaceAtBeginningOfLinePattern = regexp.MustCompile(`(?m)^\s+`)
|
|
|
|
// Yes, we bundle at runtime, give comptime pls
|
|
var bundledCSSContents = func() []byte {
|
|
const mainFilePath = "css/main.css"
|
|
|
|
var recursiveParseImports func(path string, depth int) ([]byte, error)
|
|
recursiveParseImports = func(path string, depth int) ([]byte, error) {
|
|
if depth > 20 {
|
|
return nil, errors.New("maximum import depth reached, is one of your imports circular?")
|
|
}
|
|
|
|
mainFileContents, err := readAllFromStaticFS(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Normalize line endings, otherwise the \r's make the regex not match
|
|
mainFileContents = bytes.ReplaceAll(mainFileContents, []byte("\r\n"), []byte("\n"))
|
|
|
|
mainFileDir := filepath.Dir(path)
|
|
var importLastErr error
|
|
|
|
parsed := cssImportPattern.ReplaceAllFunc(mainFileContents, func(match []byte) []byte {
|
|
if importLastErr != nil {
|
|
return nil
|
|
}
|
|
|
|
matches := cssImportPattern.FindSubmatch(match)
|
|
if len(matches) != 2 {
|
|
importLastErr = fmt.Errorf(
|
|
"import didn't return expected number of capture groups: %s, expected 2, got %d",
|
|
match, len(matches),
|
|
)
|
|
return nil
|
|
}
|
|
|
|
importFilePath := filepath.Join(mainFileDir, string(matches[1]))
|
|
importContents, err := recursiveParseImports(importFilePath, depth+1)
|
|
if err != nil {
|
|
importLastErr = err
|
|
return nil
|
|
}
|
|
|
|
return importContents
|
|
})
|
|
|
|
if importLastErr != nil {
|
|
return nil, importLastErr
|
|
}
|
|
|
|
return parsed, nil
|
|
}
|
|
|
|
contents, err := recursiveParseImports(mainFilePath, 0)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("building CSS bundle: %v", err))
|
|
}
|
|
|
|
// We could strip a bunch more unnecessary characters, but the biggest
|
|
// win comes from removing the whitespace at the beginning of lines
|
|
// since that's at least 4 bytes per property, which yielded a ~20% reduction.
|
|
contents = cssSingleLineCommentPattern.ReplaceAll(contents, nil)
|
|
contents = whitespaceAtBeginningOfLinePattern.ReplaceAll(contents, nil)
|
|
contents = bytes.ReplaceAll(contents, []byte("\n"), []byte(""))
|
|
|
|
return contents
|
|
}()
|