static.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. // Copyright 2021 Frédéric Guillot. All rights reserved.
  2. // Use of this source code is governed by the Apache 2.0
  3. // license that can be found in the LICENSE file.
  4. package static // import "miniflux.app/ui/static"
  5. import (
  6. "bytes"
  7. "crypto/sha256"
  8. "embed"
  9. "fmt"
  10. "github.com/tdewolff/minify/v2"
  11. "github.com/tdewolff/minify/v2/css"
  12. "github.com/tdewolff/minify/v2/js"
  13. )
  14. // Static assets.
  15. var (
  16. StylesheetBundleChecksums map[string]string
  17. StylesheetBundles map[string][]byte
  18. JavascriptBundleChecksums map[string]string
  19. JavascriptBundles map[string][]byte
  20. )
  21. //go:embed bin/*
  22. var binaryFiles embed.FS
  23. //go:embed css/*.css
  24. var stylesheetFiles embed.FS
  25. //go:embed js/*.js
  26. var javascriptFiles embed.FS
  27. var binaryFileChecksums map[string]string
  28. // CalculateBinaryFileChecksums generates hash of embed binary files.
  29. func CalculateBinaryFileChecksums() error {
  30. binaryFileChecksums = make(map[string]string)
  31. dirEntries, err := binaryFiles.ReadDir("bin")
  32. if err != nil {
  33. return err
  34. }
  35. for _, dirEntry := range dirEntries {
  36. data, err := LoadBinaryFile(dirEntry.Name())
  37. if err != nil {
  38. return err
  39. }
  40. binaryFileChecksums[dirEntry.Name()] = fmt.Sprintf("%x", sha256.Sum256(data))
  41. }
  42. return nil
  43. }
  44. // LoadBinaryFile loads an embed binary file.
  45. func LoadBinaryFile(filename string) ([]byte, error) {
  46. return binaryFiles.ReadFile(fmt.Sprintf(`bin/%s`, filename))
  47. }
  48. // GetBinaryFileChecksum returns a binary file checksum.
  49. func GetBinaryFileChecksum(filename string) (string, error) {
  50. if _, found := binaryFileChecksums[filename]; !found {
  51. return "", fmt.Errorf(`static: unable to find checksum for %q`, filename)
  52. }
  53. return binaryFileChecksums[filename], nil
  54. }
  55. // GenerateStylesheetsBundles creates CSS bundles.
  56. func GenerateStylesheetsBundles() error {
  57. var bundles = map[string][]string{
  58. "light_serif": {"css/light.css", "css/serif.css", "css/common.css"},
  59. "light_sans_serif": {"css/light.css", "css/sans_serif.css", "css/common.css"},
  60. "dark_serif": {"css/dark.css", "css/serif.css", "css/common.css"},
  61. "dark_sans_serif": {"css/dark.css", "css/sans_serif.css", "css/common.css"},
  62. "system_serif": {"css/system.css", "css/serif.css", "css/common.css"},
  63. "system_sans_serif": {"css/system.css", "css/sans_serif.css", "css/common.css"},
  64. }
  65. StylesheetBundles = make(map[string][]byte)
  66. StylesheetBundleChecksums = make(map[string]string)
  67. minifier := minify.New()
  68. minifier.AddFunc("text/css", css.Minify)
  69. for bundle, srcFiles := range bundles {
  70. var buffer bytes.Buffer
  71. for _, srcFile := range srcFiles {
  72. fileData, err := stylesheetFiles.ReadFile(srcFile)
  73. if err != nil {
  74. return err
  75. }
  76. buffer.Write(fileData)
  77. }
  78. minifiedData, err := minifier.Bytes("text/css", buffer.Bytes())
  79. if err != nil {
  80. return err
  81. }
  82. StylesheetBundles[bundle] = minifiedData
  83. StylesheetBundleChecksums[bundle] = fmt.Sprintf("%x", sha256.Sum256(minifiedData))
  84. }
  85. return nil
  86. }
  87. // GenerateJavascriptBundles creates JS bundles.
  88. func GenerateJavascriptBundles() error {
  89. var bundles = map[string][]string{
  90. "app": {
  91. "js/dom_helper.js",
  92. "js/touch_handler.js",
  93. "js/keyboard_handler.js",
  94. "js/request_builder.js",
  95. "js/modal_handler.js",
  96. "js/app.js",
  97. "js/bootstrap.js",
  98. },
  99. "service-worker": {
  100. "js/service_worker.js",
  101. },
  102. }
  103. var prefixes = map[string]string{
  104. "app": "(function(){'use strict';",
  105. }
  106. var suffixes = map[string]string{
  107. "app": "})();",
  108. }
  109. JavascriptBundles = make(map[string][]byte)
  110. JavascriptBundleChecksums = make(map[string]string)
  111. minifier := minify.New()
  112. minifier.AddFunc("text/javascript", js.Minify)
  113. for bundle, srcFiles := range bundles {
  114. var buffer bytes.Buffer
  115. if prefix, found := prefixes[bundle]; found {
  116. buffer.WriteString(prefix)
  117. }
  118. for _, srcFile := range srcFiles {
  119. fileData, err := javascriptFiles.ReadFile(srcFile)
  120. if err != nil {
  121. return err
  122. }
  123. buffer.Write(fileData)
  124. }
  125. if suffix, found := suffixes[bundle]; found {
  126. buffer.WriteString(suffix)
  127. }
  128. minifiedData, err := minifier.Bytes("text/javascript", buffer.Bytes())
  129. if err != nil {
  130. return err
  131. }
  132. JavascriptBundles[bundle] = minifiedData
  133. JavascriptBundleChecksums[bundle] = fmt.Sprintf("%x", sha256.Sum256(minifiedData))
  134. }
  135. return nil
  136. }