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 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
|
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package static // import "miniflux.app/v2/internal/ui/static"
import (
"bytes"
"embed"
"log/slog"
"slices"
"strings"
"miniflux.app/v2/internal/crypto"
"github.com/tdewolff/minify/v2"
"github.com/tdewolff/minify/v2/css"
"github.com/tdewolff/minify/v2/js"
"github.com/tdewolff/minify/v2/svg"
)
type asset struct {
Data []byte
Checksum string
}
// Static assets.
var (
StylesheetBundles map[string]asset
JavascriptBundles map[string]asset
BinaryBundles map[string]asset
)
//go:embed bin/*
var binaryFiles embed.FS
//go:embed css/*.css
var stylesheetFiles embed.FS
//go:embed js/*.js
var javascriptFiles embed.FS
func GenerateBinaryBundles() error {
dirEntries, err := binaryFiles.ReadDir("bin")
if err != nil {
return err
}
BinaryBundles = make(map[string]asset, len(dirEntries))
minifier := minify.New()
minifier.Add("image/svg+xml", &svg.Minifier{
KeepComments: true, // needed to keep the license
})
for _, dirEntry := range dirEntries {
name := dirEntry.Name()
data, err := binaryFiles.ReadFile("bin/" + name)
if err != nil {
return err
}
if strings.HasSuffix(name, ".svg") {
// minifier.Bytes returns the data unchanged in case of error.
data, err = minifier.Bytes("image/svg+xml", data)
if err != nil {
slog.Error("Unable to minimize the svg file", slog.String("filename", name), slog.Any("error", err))
}
}
BinaryBundles[name] = asset{
Data: data,
Checksum: crypto.HashFromBytes(data),
}
}
return nil
}
// GenerateStylesheetsBundles creates CSS bundles.
func GenerateStylesheetsBundles() error {
var bundles = map[string][]string{
"light_serif": {"css/light.css", "css/serif.css", "css/common.css"},
"light_sans_serif": {"css/light.css", "css/sans_serif.css", "css/common.css"},
"dark_serif": {"css/dark.css", "css/serif.css", "css/common.css"},
"dark_sans_serif": {"css/dark.css", "css/sans_serif.css", "css/common.css"},
"system_serif": {"css/system.css", "css/serif.css", "css/common.css"},
"system_sans_serif": {"css/system.css", "css/sans_serif.css", "css/common.css"},
}
StylesheetBundles = make(map[string]asset, len(bundles))
minifier := minify.New()
minifier.AddFunc("text/css", css.Minify)
for bundle, srcFiles := range bundles {
var buffer bytes.Buffer
for _, srcFile := range srcFiles {
fileData, err := stylesheetFiles.ReadFile(srcFile)
if err != nil {
return err
}
buffer.Write(fileData)
}
minifiedData, err := minifier.Bytes("text/css", buffer.Bytes())
if err != nil {
return err
}
StylesheetBundles[bundle] = asset{
Data: minifiedData,
Checksum: crypto.HashFromBytes(minifiedData),
}
}
return nil
}
// GenerateJavascriptBundles creates JS bundles.
func GenerateJavascriptBundles(webauthnEnabled bool) error {
var bundles = map[string][]string{
"app": {
"js/touch_handler.js",
"js/keyboard_handler.js",
"js/app.js",
},
"service-worker": {
"js/service_worker.js",
},
}
if webauthnEnabled {
bundles["app"] = slices.Insert(bundles["app"], 1, "js/webauthn_handler.js")
}
JavascriptBundles = make(map[string]asset, len(bundles))
jsMinifier := js.Minifier{Version: 2020}
minifier := minify.New()
minifier.AddFunc("text/javascript", jsMinifier.Minify)
for bundle, srcFiles := range bundles {
var buffer bytes.Buffer
for _, srcFile := range srcFiles {
fileData, err := javascriptFiles.ReadFile(srcFile)
if err != nil {
return err
}
buffer.Write(fileData)
}
minifiedData, err := minifier.Bytes("text/javascript", buffer.Bytes())
if err != nil {
return err
}
JavascriptBundles[bundle] = asset{
Data: minifiedData,
Checksum: crypto.HashFromBytes(minifiedData),
}
}
return nil
}
|