generate.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  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, srcFiles []string) {
  80. var b strings.Builder
  81. b.WriteString("(function() {'use strict';")
  82. b.WriteString(concat(srcFiles))
  83. b.WriteString("})();")
  84. m := minify.New()
  85. m.AddFunc("text/javascript", js.Minify)
  86. output, err := m.String("text/javascript", b.String())
  87. if err != nil {
  88. panic(err)
  89. }
  90. bundle := NewBundle("static", "Javascript")
  91. bundle.Files["app"] = output
  92. bundle.Checksums["app"] = checksum([]byte(output))
  93. bundle.Write(bundleFile)
  94. }
  95. func generateCSSBundle(bundleFile string, themes map[string][]string) {
  96. bundle := NewBundle("static", "Stylesheets")
  97. m := minify.New()
  98. m.AddFunc("text/css", css.Minify)
  99. for theme, srcFiles := range themes {
  100. data := concat(srcFiles)
  101. minifiedData, err := m.String("text/css", data)
  102. if err != nil {
  103. panic(err)
  104. }
  105. bundle.Files[theme] = minifiedData
  106. bundle.Checksums[theme] = checksum([]byte(minifiedData))
  107. }
  108. bundle.Write(bundleFile)
  109. }
  110. func generateBinaryBundle(bundleFile string, srcFiles []string) {
  111. bundle := NewBundle("static", "Binaries")
  112. for _, srcFile := range srcFiles {
  113. data := readFile(srcFile)
  114. filename := basename(srcFile)
  115. encodedData := base64.StdEncoding.EncodeToString(data)
  116. bundle.Files[filename] = string(encodedData)
  117. bundle.Checksums[filename] = checksum(data)
  118. }
  119. bundle.Write(bundleFile)
  120. }
  121. func generateBundle(bundleFile, pkg, mapName string, srcFiles []string) {
  122. bundle := NewBundle(pkg, mapName)
  123. for _, srcFile := range srcFiles {
  124. data := readFile(srcFile)
  125. filename := stripExtension(basename(srcFile))
  126. bundle.Files[filename] = string(data)
  127. bundle.Checksums[filename] = checksum(data)
  128. }
  129. bundle.Write(bundleFile)
  130. }
  131. func main() {
  132. generateJSBundle("ui/static/js.go", []string{
  133. "ui/static/js/dom_helper.js",
  134. "ui/static/js/touch_handler.js",
  135. "ui/static/js/keyboard_handler.js",
  136. "ui/static/js/mouse_handler.js",
  137. "ui/static/js/form_handler.js",
  138. "ui/static/js/request_builder.js",
  139. "ui/static/js/unread_counter_handler.js",
  140. "ui/static/js/entry_handler.js",
  141. "ui/static/js/confirm_handler.js",
  142. "ui/static/js/menu_handler.js",
  143. "ui/static/js/modal_handler.js",
  144. "ui/static/js/nav_handler.js",
  145. "ui/static/js/bootstrap.js",
  146. })
  147. generateCSSBundle("ui/static/css.go", map[string][]string{
  148. "default": []string{"ui/static/css/common.css"},
  149. "black": []string{"ui/static/css/common.css", "ui/static/css/black.css"},
  150. "sansserif": []string{"ui/static/css/common.css", "ui/static/css/sansserif.css"},
  151. })
  152. generateBinaryBundle("ui/static/bin.go", glob("ui/static/bin/*"))
  153. generateBundle("sql/sql.go", "sql", "SqlMap", glob("sql/*.sql"))
  154. generateBundle("template/views.go", "template", "templateViewsMap", glob("template/html/*.html"))
  155. generateBundle("template/common.go", "template", "templateCommonMap", glob("template/html/common/*.html"))
  156. generateBundle("locale/translations.go", "locale", "translations", glob("locale/translations/*.json"))
  157. }