generate.go 5.2 KB

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