generate.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. // Copyright 2017 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. // +build ignore
  5. package main
  6. import (
  7. "crypto/sha256"
  8. "encoding/base64"
  9. "fmt"
  10. "io/ioutil"
  11. "os"
  12. "path"
  13. "path/filepath"
  14. "strings"
  15. "text/template"
  16. "github.com/tdewolff/minify"
  17. "github.com/tdewolff/minify/css"
  18. "github.com/tdewolff/minify/js"
  19. )
  20. const tpl = `// Code generated by go generate; DO NOT EDIT.
  21. package {{ .Package }} // import "miniflux.app/{{ .ImportPath }}"
  22. var {{ .Map }} = map[string]string{
  23. {{ range $constant, $content := .Files }}` + "\t" + `"{{ $constant }}": ` + "`{{ $content }}`" + `,
  24. {{ end }}}
  25. var {{ .Map }}Checksums = map[string]string{
  26. {{ range $constant, $content := .Checksums }}` + "\t" + `"{{ $constant }}": "{{ $content }}",
  27. {{ end }}}
  28. `
  29. var bundleTpl = template.Must(template.New("").Parse(tpl))
  30. type Bundle struct {
  31. Package string
  32. Map string
  33. ImportPath string
  34. Files map[string]string
  35. Checksums map[string]string
  36. }
  37. func (b *Bundle) Write(filename string) {
  38. f, err := os.Create(filename)
  39. if err != nil {
  40. panic(err)
  41. }
  42. defer f.Close()
  43. bundleTpl.Execute(f, b)
  44. }
  45. func NewBundle(pkg, mapName, importPath string) *Bundle {
  46. return &Bundle{
  47. Package: pkg,
  48. Map: mapName,
  49. ImportPath: importPath,
  50. Files: make(map[string]string),
  51. Checksums: make(map[string]string),
  52. }
  53. }
  54. func readFile(filename string) []byte {
  55. data, err := ioutil.ReadFile(filename)
  56. if err != nil {
  57. panic(err)
  58. }
  59. return data
  60. }
  61. func checksum(data []byte) string {
  62. return fmt.Sprintf("%x", sha256.Sum256(data))
  63. }
  64. func basename(filename string) string {
  65. return path.Base(filename)
  66. }
  67. func stripExtension(filename string) string {
  68. filename = strings.TrimSuffix(filename, filepath.Ext(filename))
  69. return strings.Replace(filename, " ", "_", -1)
  70. }
  71. func glob(pattern string) []string {
  72. files, _ := filepath.Glob(pattern)
  73. return files
  74. }
  75. func concat(files []string) string {
  76. var b strings.Builder
  77. for _, file := range files {
  78. b.Write(readFile(file))
  79. }
  80. return b.String()
  81. }
  82. func generateJSBundle(bundleFile string, bundleFiles map[string][]string, prefixes, suffixes map[string]string) {
  83. bundle := NewBundle("static", "Javascripts", "ui/static")
  84. m := minify.New()
  85. m.AddFunc("text/javascript", js.Minify)
  86. for name, srcFiles := range bundleFiles {
  87. var b strings.Builder
  88. if prefix, found := prefixes[name]; found {
  89. b.WriteString(prefix)
  90. }
  91. b.WriteString(concat(srcFiles))
  92. if suffix, found := suffixes[name]; found {
  93. b.WriteString(suffix)
  94. }
  95. minifiedData, err := m.String("text/javascript", b.String())
  96. if err != nil {
  97. panic(err)
  98. }
  99. bundle.Files[name] = minifiedData
  100. bundle.Checksums[name] = checksum([]byte(minifiedData))
  101. }
  102. bundle.Write(bundleFile)
  103. }
  104. func generateCSSBundle(bundleFile string, themes map[string][]string) {
  105. bundle := NewBundle("static", "Stylesheets", "ui/static")
  106. m := minify.New()
  107. m.AddFunc("text/css", css.Minify)
  108. for theme, srcFiles := range themes {
  109. data := concat(srcFiles)
  110. minifiedData, err := m.String("text/css", data)
  111. if err != nil {
  112. panic(err)
  113. }
  114. bundle.Files[theme] = minifiedData
  115. bundle.Checksums[theme] = checksum([]byte(minifiedData))
  116. }
  117. bundle.Write(bundleFile)
  118. }
  119. func generateBinaryBundle(bundleFile string, srcFiles []string) {
  120. bundle := NewBundle("static", "Binaries", "ui/static")
  121. for _, srcFile := range srcFiles {
  122. data := readFile(srcFile)
  123. filename := basename(srcFile)
  124. encodedData := base64.StdEncoding.EncodeToString(data)
  125. bundle.Files[filename] = string(encodedData)
  126. bundle.Checksums[filename] = checksum(data)
  127. }
  128. bundle.Write(bundleFile)
  129. }
  130. func generateBundle(bundleFile, pkg, mapName string, srcFiles []string) {
  131. bundle := NewBundle(pkg, mapName, pkg)
  132. for _, srcFile := range srcFiles {
  133. data := readFile(srcFile)
  134. filename := stripExtension(basename(srcFile))
  135. bundle.Files[filename] = string(data)
  136. bundle.Checksums[filename] = checksum(data)
  137. }
  138. bundle.Write(bundleFile)
  139. }
  140. func main() {
  141. generateJSBundle("ui/static/js.go", map[string][]string{
  142. "app": []string{
  143. "ui/static/js/dom_helper.js",
  144. "ui/static/js/touch_handler.js",
  145. "ui/static/js/keyboard_handler.js",
  146. "ui/static/js/mouse_handler.js",
  147. "ui/static/js/form_handler.js",
  148. "ui/static/js/request_builder.js",
  149. "ui/static/js/unread_counter_handler.js",
  150. "ui/static/js/entry_handler.js",
  151. "ui/static/js/confirm_handler.js",
  152. "ui/static/js/menu_handler.js",
  153. "ui/static/js/modal_handler.js",
  154. "ui/static/js/nav_handler.js",
  155. "ui/static/js/bootstrap.js",
  156. },
  157. "sw": []string{
  158. "ui/static/js/sw.js",
  159. },
  160. }, map[string]string{
  161. "app": "(function(){'use strict';",
  162. "sw": "'use strict';",
  163. }, map[string]string{
  164. "app": "})();",
  165. })
  166. generateCSSBundle("ui/static/css.go", map[string][]string{
  167. "default": []string{"ui/static/css/common.css"},
  168. "black": []string{"ui/static/css/common.css", "ui/static/css/black.css"},
  169. "sansserif": []string{"ui/static/css/common.css", "ui/static/css/sansserif.css"},
  170. })
  171. generateBinaryBundle("ui/static/bin.go", glob("ui/static/bin/*"))
  172. generateBundle("database/sql.go", "database", "SqlMap", glob("database/sql/*.sql"))
  173. generateBundle("template/views.go", "template", "templateViewsMap", glob("template/html/*.html"))
  174. generateBundle("template/common.go", "template", "templateCommonMap", glob("template/html/common/*.html"))
  175. generateBundle("locale/translations.go", "locale", "translations", glob("locale/translations/*.json"))
  176. }