Files
tamabox/internal/template/minify.go
T
2026-09-08 10:10:44 +08:00

120 lines
2.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright 2022 E99p1ant. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package template
import (
"bytes"
"embed"
"io/fs"
"path/filepath"
"regexp"
"github.com/flamego/template"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/tdewolff/minify/v2"
"github.com/tdewolff/minify/v2/css"
"github.com/tdewolff/minify/v2/html"
"github.com/tdewolff/minify/v2/js"
)
var _ template.FileSystem = (*FileSystem)(nil)
type file struct {
name string
data []byte
ext string
}
func (f *file) Name() string { return f.name }
func (f *file) Data() ([]byte, error) { return f.data, nil }
func (f *file) Ext() string { return f.ext }
type FileSystem struct {
files []template.File
}
func (fs *FileSystem) Files() []template.File { return fs.files }
// getExt returns the extension of given name, prefixed with the dot (".").
// 必须用 filepath.Ext:取最后一个点之后的部分。
// 旧的 strings.Index 实现会把 "foo.bar.html" 的扩展名误判为 ".bar.html",
// 导致模板名截取错误(如 "foo" 丢失子目录结构)。
func getExt(name string) string {
return filepath.Ext(name)
}
func Minify(embedFS embed.FS, dir string, allowedExtensions []string) (*FileSystem, error) {
var beforeSize, afterSize int64
var files []template.File
m := minify.New()
m.AddFunc("text/html", html.Minify)
m.AddFunc("text/css", css.Minify)
m.AddFuncRegexp(regexp.MustCompile(`^(application|text)/(x-)?(java|ecma)script$`), js.Minify)
err := fs.WalkDir(embedFS, dir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
relpath, err := filepath.Rel(dir, path)
if err != nil {
return errors.Wrap(err, "get relative path")
}
ext := getExt(relpath)
for _, allowed := range allowedExtensions {
if ext != allowed {
continue
}
data, err := embedFS.ReadFile(path)
if err != nil {
return errors.Wrap(err, "read")
}
beforeSize += int64(len(data))
var buf bytes.Buffer
switch filepath.Ext(path) {
case ".html":
if err := html.Minify(m, &buf, bytes.NewReader(data), nil); err != nil {
return errors.Wrap(err, "minify html")
}
default:
buf = *bytes.NewBuffer(data)
}
afterSize += int64(buf.Len())
name := filepath.ToSlash(relpath[:len(relpath)-len(ext)])
files = append(files,
&file{
name: name,
data: buf.Bytes(),
ext: ext,
},
)
}
return nil
})
if err != nil {
return nil, errors.Wrap(err, "walk")
}
logrus.WithFields(logrus.Fields{
"before_size": beforeSize,
"after_size": afterSize,
"ratio": float64(afterSize) / float64(beforeSize),
}).Info("Minify succeeded")
return &FileSystem{
files: files,
}, nil
}