diff --git a/client/package.json b/client/package.json index c36f8839..eaba9eee 100644 --- a/client/package.json +++ b/client/package.json @@ -85,10 +85,9 @@ "test": "jest", "test:verbose": "jest --verbose", "test:watch": "jest --watch", - "gen:install": "GO111MODULE=off go get -u github.com/andyleap/gencode github.com/mailru/easyjson/... github.com/SlinSo/egon/cmd/egon", + "gen:install": "GO111MODULE=off go get -u github.com/andyleap/gencode github.com/mailru/easyjson/...", "gen:binary": "gencode go -package storage -schema ../storage/storage.schema -unsafe", - "gen:json": "easyjson -all -lower_camel_case -omit_empty ../server/json.go ../server/index_data.go && easyjson -lower_camel_case -omit_empty ../storage/user.go", - "gen:template": "egon -s -m ../server" + "gen:json": "easyjson -all -lower_camel_case -omit_empty ../server/json.go ../server/index_data.go && easyjson -lower_camel_case -omit_empty ../storage/user.go" }, "jest": { "moduleNameMapper": { diff --git a/go.mod b/go.mod index 9ff0d109..e89a7842 100644 --- a/go.mod +++ b/go.mod @@ -47,10 +47,11 @@ require ( github.com/steveyen/gtreap v0.0.0-20150807155958-0abe01ef9be2 // indirect github.com/stretchr/testify v1.2.2 github.com/syndtr/goleveldb v0.0.0-20181128100959-b001fa50d6b2 // indirect + github.com/tdewolff/minify/v2 v2.3.8 github.com/tecbot/gorocksdb v0.0.0-20181010114359-8752a9433481 // indirect github.com/tinylib/msgp v1.1.0 // indirect github.com/willf/bitset v1.1.9 // indirect - github.com/xenolf/lego v1.2.2-0.20181211001449-b05b54d1f69a + github.com/xenolf/lego v1.2.2-0.20181211001449-b05b54d1f69a // indirect golang.org/x/net v0.0.0-20181213202711-891ebc4b82d6 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f // indirect golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06 // indirect diff --git a/go.sum b/go.sum index 0d92dad3..e350dfe4 100644 --- a/go.sum +++ b/go.sum @@ -13,6 +13,7 @@ github.com/blevesearch/segment v0.0.0-20160915185041-762005e7a34f h1:kqbi9lqXLLs github.com/blevesearch/segment v0.0.0-20160915185041-762005e7a34f/go.mod h1:IInt5XRvpiGE09KOk9mmCMLjHhydIhNPKPPFLFBB7L8= github.com/boltdb/bolt v0.0.0-20180302180052-fd01fc79c553 h1:yvSJ8qbaWLeS7COhu2KJ0epn4mmc+aGeBP7Dpg7xQTY= github.com/boltdb/bolt v0.0.0-20180302180052-fd01fc79c553/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -28,6 +29,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dsnet/compress v0.0.0-20171208185109-cc9eb1d7ad76 h1:eX+pdPPlD279OWgdx7f6KqIRSONuK7egk+jDx7OM3Ac= github.com/dsnet/compress v0.0.0-20171208185109-cc9eb1d7ad76/go.mod h1:KjxHHirfLaw19iGT70HvVjHQsL1vq1SRQB4yOsAfy2s= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ= @@ -75,6 +77,7 @@ github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDe github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs= github.com/mholt/certmagic v0.0.0-20181214183619-fd326512c10c h1:WBDm4CVARtUhxBQZa6g/llfZtO7Sf1qMkCWCxqpXNvI= github.com/mholt/certmagic v0.0.0-20181214183619-fd326512c10c/go.mod h1:09k100NW9m2nUn/lUB5JmCG1iFw0nEZ4HT0bdhDn9ns= github.com/miekg/dns v1.1.1 h1:DVkblRdiScEnEr0LR9nTnEQqHYycjkXW9bOjd+2EL2o= @@ -121,6 +124,12 @@ github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/syndtr/goleveldb v0.0.0-20181128100959-b001fa50d6b2 h1:GnOzE5fEFN3b2zDhJJABEofdb51uMRNb8eqIVtdducs= github.com/syndtr/goleveldb v0.0.0-20181128100959-b001fa50d6b2/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= +github.com/tdewolff/minify/v2 v2.3.8 h1:Eyv23Tu+Rb5Q2vyxmvzUgtHetgneqAsaGv3950s1EeA= +github.com/tdewolff/minify/v2 v2.3.8/go.mod h1:DD1stRlSx6JsHfl1+E/HVMQeXiec9rD1UQ0epklIZLc= +github.com/tdewolff/parse/v2 v2.3.5 h1:/uS8JfhwVJsNkEh769GM5ENv6L9LOh2Z9uW3tCdlhs0= +github.com/tdewolff/parse/v2 v2.3.5/go.mod h1:HansaqmN4I/U7L6/tUp0NcwT2tFO0F4EAWYGSDzkYNk= +github.com/tdewolff/test v1.0.0 h1:jOwzqCXr5ePXEPGJaq2ivoR6HOCi+D5TPfpoyg8yvmU= +github.com/tdewolff/test v1.0.0/go.mod h1:DiQUlutnqlEvdvhSn2LPGy4TFwRauAaYDsL+683RNX4= github.com/tecbot/gorocksdb v0.0.0-20181010114359-8752a9433481 h1:HOxvxvnntLiPn123Fk+twfUhCQdMDaqmb0cclArW0T0= github.com/tecbot/gorocksdb v0.0.0-20181010114359-8752a9433481/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8= github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU= @@ -141,6 +150,7 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181031143558-9b800f95dbbc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06 h1:0oC8rFnE+74kEmuHZ46F6KHsMr5Gx2gUQPuNz28iQZM= golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/server/index.egon.go b/server/index.egon.go deleted file mode 100644 index 923f86de..00000000 --- a/server/index.egon.go +++ /dev/null @@ -1,26 +0,0 @@ -// Generated by egon. -// 🚫Edit at your own risk. - -package server -import ( -"io" -) - -func IndexTemplate(w io.Writer, cssPath string, inlineScript string, scripts []string) error { -io.WriteString(w, "Dispatch") - if cssPath != "" { -io.WriteString(w, "") - } -io.WriteString(w, "
") - for _, script := range scripts { -io.WriteString(w, "") - } -io.WriteString(w, "") -return nil -} diff --git a/server/index.egon b/server/index.go similarity index 73% rename from server/index.egon rename to server/index.go index 99113d18..fccc6e68 100644 --- a/server/index.egon +++ b/server/index.go @@ -1,5 +1,12 @@ -<%! cssPath string, inlineScript string, scripts []string %> +package server +type indexTemplateData struct { + InlineScript string + CSSPath string + Scripts []string +} + +const indexTemplate = ` @@ -13,9 +20,9 @@ - + {{if .InlineScript}} + + {{end}} @@ -23,9 +30,9 @@ - <% if cssPath != "" { %> - - <% } %> + {{if .CSSPath}} + + {{end}} @@ -33,11 +40,11 @@
- <% for _, script := range scripts { %> - - <% } %> + {{range .Scripts}} + + {{end}} - +` diff --git a/server/serve_files.go b/server/serve_files.go index a210b660..361f233e 100644 --- a/server/serve_files.go +++ b/server/serve_files.go @@ -12,18 +12,23 @@ import ( "path/filepath" "strconv" "strings" + "text/template" "time" "github.com/dsnet/compress/brotli" "github.com/khlieng/dispatch/assets" + "github.com/tdewolff/minify/v2" + "github.com/tdewolff/minify/v2/html" ) const longCacheControl = "public, max-age=31536000, immutable" const disabledCacheControl = "no-cache, no-store, must-revalidate" type File struct { - Asset string - GzipAsset []byte + Data []byte + Length string + GzipData []byte + GzipLength string Hash string ContentType string CacheControl string @@ -45,9 +50,8 @@ func newH2PushAsset(name string) h2PushAsset { var ( files = map[string]*File{} - indexStylesheet string - indexScripts []string - inlineScript string + indexPage []byte + indexPageLen string inlineScriptSha256 string serviceWorker []byte @@ -75,20 +79,22 @@ func (d *Dispatch) initFileServer() { cfg := d.Config() if cfg.Dev { - indexScripts = []string{"boot.js", "main.js"} + renderIndexPage(indexTemplateData{ + Scripts: []string{"boot.js", "main.js"}, + }) } else { bootloader := decompressedAsset(findAssetName("boot*.js")) runtime := decompressedAsset(findAssetName("runtime*.js")) - inlineScript = string(bootloader) + string(runtime) + inlineScript := string(bootloader) + string(runtime) hash := sha256.New() hash.Write(bootloader) hash.Write(runtime) inlineScriptSha256 = base64.StdEncoding.EncodeToString(hash.Sum(nil)) - indexStylesheet = findAssetName("main*.css") - indexScripts = []string{ + indexStylesheet := findAssetName("main*.css") + indexScripts := []string{ findAssetName("vendors*.js"), findAssetName("main*.js"), } @@ -120,27 +126,33 @@ func (d *Dispatch) initFileServer() { } file := &File{ - Asset: asset, ContentType: contentTypes[filepath.Ext(assetName)], CacheControl: longCacheControl, Compressed: strings.HasSuffix(asset, ".br"), } - if file.Compressed { - data, err := assets.Asset(file.Asset) - if err != nil { - log.Fatal(err) - } + data, err := assets.Asset(asset) + fatalErr(err) + file.Data = data + file.Length = strconv.Itoa(len(data)) - file.GzipAsset = gzipAsset(data) + if file.Compressed { + file.GzipData = gzipAsset(data) + file.GzipLength = strconv.Itoa(len(file.GzipData)) } files["/"+assetName] = file } + renderIndexPage(indexTemplateData{ + CSSPath: indexStylesheet, + InlineScript: inlineScript, + Scripts: indexScripts, + }) + serviceWorker = decompressedAsset("sw.js") hash.Reset() - IndexTemplate(hash, indexStylesheet, inlineScript, indexScripts) + hash.Write(indexPage) indexHash := base64.StdEncoding.EncodeToString(hash.Sum(nil)) serviceWorker = append(serviceWorker, []byte(` @@ -165,6 +177,27 @@ workbox.routing.registerNavigationRoute('/');`)...) } } +func renderIndexPage(data indexTemplateData) { + tmpl, err := template.New("").Parse(indexTemplate) + fatalErr(err) + + m := minify.New() + m.AddFunc("text/html", html.Minify) + + buf := &bytes.Buffer{} + gzw, err := gzip.NewWriterLevel(buf, gzip.BestCompression) + fatalErr(err) + mw := m.Writer("text/html", gzw) + + fatalErr(tmpl.Execute(mw, data)) + + fatalErr(mw.Close()) + fatalErr(gzw.Close()) + + indexPage = buf.Bytes() + indexPageLen = strconv.Itoa(len(indexPage)) +} + func findAssetName(glob string) string { for _, assetName := range assets.AssetNames() { assetName = strings.TrimSuffix(assetName, ".br") @@ -178,9 +211,7 @@ func findAssetName(glob string) string { func decompressAsset(data []byte) []byte { br, err := brotli.NewReader(bytes.NewReader(data), nil) - if err != nil { - log.Fatal(err) - } + fatalErr(err) buf := &bytes.Buffer{} io.Copy(buf, br) @@ -189,23 +220,17 @@ func decompressAsset(data []byte) []byte { func decompressedAsset(name string) []byte { asset, err := assets.Asset(name + ".br") - if err != nil { - log.Fatal(err) - } + fatalErr(err) return decompressAsset(asset) } func gzipAsset(data []byte) []byte { br, err := brotli.NewReader(bytes.NewReader(data), nil) - if err != nil { - log.Fatal(err) - } + fatalErr(err) buf := &bytes.Buffer{} gzw, err := gzip.NewWriterLevel(buf, gzip.BestCompression) - if err != nil { - log.Fatal(err) - } + fatalErr(err) io.Copy(gzw, br) gzw.Close() @@ -215,30 +240,20 @@ func gzipAsset(data []byte) []byte { func (d *Dispatch) serveFiles(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/" { d.serveIndex(w, r) - return - } - - if file, ok := files[r.URL.Path]; ok { + } else if file, ok := files[r.URL.Path]; ok { d.serveFile(w, r, file) - return - } - - if r.URL.Path == "/sw.js" { + } else if r.URL.Path == "/sw.js" { w.Header().Set("Cache-Control", disabledCacheControl) w.Header().Set("Content-Type", "text/javascript") w.Header().Set("Content-Length", strconv.Itoa(len(serviceWorker))) w.Write(serviceWorker) - return - } - - if r.URL.Path == "/robots.txt" { + } else if r.URL.Path == "/robots.txt" { w.Header().Set("Content-Type", "text/plain") w.Header().Set("Content-Length", strconv.Itoa(len(robots))) w.Write(robots) - return + } else { + d.serveIndex(w, r) } - - d.serveIndex(w, r) } func (d *Dispatch) serveIndex(w http.ResponseWriter, r *http.Request) { @@ -310,12 +325,10 @@ func (d *Dispatch) serveIndex(w http.ResponseWriter, r *http.Request) { if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") { w.Header().Set("Content-Encoding", "gzip") - - gzw := getGzipWriter(w) - IndexTemplate(gzw, indexStylesheet, inlineScript, indexScripts) - putGzipWriter(gzw) + w.Header().Set("Content-Length", indexPageLen) + w.Write(indexPage) } else { - IndexTemplate(w, indexStylesheet, inlineScript, indexScripts) + serveDecompressed(w, indexPage) } } @@ -331,38 +344,39 @@ func setPushCookie(w http.ResponseWriter, r *http.Request) { } func (d *Dispatch) serveFile(w http.ResponseWriter, r *http.Request, file *File) { - data, err := assets.Asset(file.Asset) + w.Header().Set("Cache-Control", file.CacheControl) + w.Header().Set("Content-Type", file.ContentType) + + if file.Compressed && strings.Contains(r.Header.Get("Accept-Encoding"), "br") { + w.Header().Set("Content-Encoding", "br") + w.Header().Set("Content-Length", file.Length) + w.Write(file.Data) + } else if file.Compressed && strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") { + w.Header().Set("Content-Encoding", "gzip") + w.Header().Set("Content-Length", file.GzipLength) + w.Write(file.GzipData) + } else if !file.Compressed { + w.Header().Set("Content-Length", file.Length) + w.Write(file.Data) + } else { + serveDecompressed(w, file.GzipData) + } +} + +func serveDecompressed(w http.ResponseWriter, asset []byte) { + gzr, err := gzip.NewReader(bytes.NewReader(asset)) + buf, err := ioutil.ReadAll(gzr) if err != nil { http.Error(w, "", http.StatusInternalServerError) return } - if file.CacheControl != "" { - w.Header().Set("Cache-Control", file.CacheControl) - } + w.Header().Set("Content-Length", strconv.Itoa(len(buf))) + w.Write(buf) +} - w.Header().Set("Content-Type", file.ContentType) - - if file.Compressed && strings.Contains(r.Header.Get("Accept-Encoding"), "br") { - w.Header().Set("Content-Encoding", "br") - w.Header().Set("Content-Length", strconv.Itoa(len(data))) - w.Write(data) - } else if file.Compressed && strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") { - w.Header().Set("Content-Encoding", "gzip") - w.Header().Set("Content-Length", strconv.Itoa(len(file.GzipAsset))) - w.Write(file.GzipAsset) - } else if !file.Compressed { - w.Header().Set("Content-Length", strconv.Itoa(len(data))) - w.Write(data) - } else { - gzr, err := gzip.NewReader(bytes.NewReader(file.GzipAsset)) - buf, err := ioutil.ReadAll(gzr) - if err != nil { - http.Error(w, "", http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Length", strconv.Itoa(len(buf))) - w.Write(buf) +func fatalErr(err error) { + if err != nil { + log.Fatal(err) } } diff --git a/vendor/github.com/tdewolff/minify/v2/.gitattributes b/vendor/github.com/tdewolff/minify/v2/.gitattributes new file mode 100644 index 00000000..4c50ee14 --- /dev/null +++ b/vendor/github.com/tdewolff/minify/v2/.gitattributes @@ -0,0 +1 @@ +benchmarks/sample_* linguist-generated=true diff --git a/vendor/github.com/tdewolff/minify/v2/.gitignore b/vendor/github.com/tdewolff/minify/v2/.gitignore new file mode 100644 index 00000000..3f3e864b --- /dev/null +++ b/vendor/github.com/tdewolff/minify/v2/.gitignore @@ -0,0 +1,4 @@ +dist/ +benchmarks/* +!benchmarks/*.go +!benchmarks/sample_* diff --git a/vendor/github.com/tdewolff/minify/v2/.goreleaser.yml b/vendor/github.com/tdewolff/minify/v2/.goreleaser.yml new file mode 100644 index 00000000..2575f02a --- /dev/null +++ b/vendor/github.com/tdewolff/minify/v2/.goreleaser.yml @@ -0,0 +1,29 @@ +builds: + - binary: minify + main: ./cmd/minify/ + ldflags: -s -w -X main.Version={{.Version}} -X main.Commit={{.Commit}} -X main.Date={{.Date}} + env: + - CGO_ENABLED=0 + - GO111MODULE=on + goos: + - linux + - windows + - darwin + - freebsd + - netbsd + - openbsd + goarch: + - amd64 +archive: + format: tar.gz + format_overrides: + - goos: windows + format: zip + name_template: "{{.Binary}}_{{.Version}}_{{.Os}}_{{.Arch}}" + files: + - README.md + - LICENSE.md +snapshot: + name_template: "devel" +release: + disable: true diff --git a/vendor/github.com/tdewolff/minify/v2/.travis.yml b/vendor/github.com/tdewolff/minify/v2/.travis.yml new file mode 100644 index 00000000..4c14dfb2 --- /dev/null +++ b/vendor/github.com/tdewolff/minify/v2/.travis.yml @@ -0,0 +1,5 @@ +language: go +before_install: + - go get github.com/mattn/goveralls +script: + - goveralls -v -service travis-ci -repotoken $COVERALLS_TOKEN -ignore=cmd/minify/* || go test -v ./... diff --git a/vendor/github.com/tdewolff/minify/v2/LICENSE.md b/vendor/github.com/tdewolff/minify/v2/LICENSE.md new file mode 100644 index 00000000..41677de4 --- /dev/null +++ b/vendor/github.com/tdewolff/minify/v2/LICENSE.md @@ -0,0 +1,22 @@ +Copyright (c) 2015 Taco de Wolff + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/tdewolff/minify/v2/README.md b/vendor/github.com/tdewolff/minify/v2/README.md new file mode 100644 index 00000000..30080c87 --- /dev/null +++ b/vendor/github.com/tdewolff/minify/v2/README.md @@ -0,0 +1,597 @@ +# Minify [![Build Status](https://travis-ci.org/tdewolff/minify.svg?branch=master)](https://travis-ci.org/tdewolff/minify) [![GoDoc](http://godoc.org/github.com/tdewolff/minify?status.svg)](http://godoc.org/github.com/tdewolff/minify) [![Coverage Status](https://coveralls.io/repos/github/tdewolff/minify/badge.svg?branch=master)](https://coveralls.io/github/tdewolff/minify?branch=master) [![Join the chat at https://gitter.im/tdewolff/minify](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/tdewolff/minify?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +***BE AWARE: YOU NEED GO VERSION 1.9.7+, 1.10.3+, 1.11.X to run the latest release, otherwise use minify@v2.3.6 and parse@v2.3.4*** + +--- + +**[Online demo](https://go.tacodewolff.nl/minify) if you need to minify files *now*.** + +**[Command line tool](https://github.com/tdewolff/minify/tree/master/cmd/minify) that minifies concurrently and supports watching file changes.** + +**[All releases](https://github.com/tdewolff/minify/releases) for various platforms.** + +--- + +Minify is a minifier package written in [Go][1]. It provides HTML5, CSS3, JS, JSON, SVG and XML minifiers and an interface to implement any other minifier. Minification is the process of removing bytes from a file (such as whitespace) without changing its output and therefore shrinking its size and speeding up transmission over the internet and possibly parsing. The implemented minifiers are designed for high performance. + +The core functionality associates mimetypes with minification functions, allowing embedded resources (like CSS or JS within HTML files) to be minified as well. Users can add new implementations that are triggered based on a mimetype (or pattern), or redirect to an external command (like ClosureCompiler, UglifyCSS, ...). + +#### Table of Contents + +- [Minify](#minify) + - [Prologue](#prologue) + - [Installation](#installation) + - [API stability](#api-stability) + - [Testing](#testing) + - [Performance](#performance) + - [HTML](#html) + - [Whitespace removal](#whitespace-removal) + - [CSS](#css) + - [JS](#js) + - [JSON](#json) + - [SVG](#svg) + - [XML](#xml) + - [Usage](#usage) + - [New](#new) + - [From reader](#from-reader) + - [From bytes](#from-bytes) + - [From string](#from-string) + - [To reader](#to-reader) + - [To writer](#to-writer) + - [Middleware](#middleware) + - [Custom minifier](#custom-minifier) + - [Mediatypes](#mediatypes) + - [Examples](#examples) + - [Common minifiers](#common-minifiers) + - [Custom minifier](#custom-minifier-example) + - [ResponseWriter](#responsewriter) + - [Templates](#templates) + - [License](#license) + +### Status + +* CSS: **fully implemented** +* HTML: **fully implemented** +* JS: improved JSmin implementation +* JSON: **fully implemented** +* SVG: partially implemented; in development +* XML: **fully implemented** + +### Roadmap + +- [ ] General speed-up of all minifiers (use ASM for whitespace funcs) +- [ ] Improve JS minifiers by shortening variables and proper semicolon omission +- [ ] Speed-up SVG minifier, it is very slow +- [x] Proper parser error reporting and line number + column information +- [ ] Generation of source maps (uncertain, might slow down parsers too much if it cannot run separately nicely) +- [ ] Look into compression of images, fonts and other web resources (into package `compress`)? +- [ ] Create a cmd to pack webfiles (much like webpack), ie. merging CSS and JS files, inlining small external files, minification and gzipping. This would work on HTML files. +- [ ] Create a package to format files, much like `gofmt` for Go files? + +## Prologue +Minifiers or bindings to minifiers exist in almost all programming languages. Some implementations are merely using several regular-expressions to trim whitespace and comments (even though regex for parsing HTML/XML is ill-advised, for a good read see [Regular Expressions: Now You Have Two Problems](http://blog.codinghorror.com/regular-expressions-now-you-have-two-problems/)). Some implementations are much more profound, such as the [YUI Compressor](http://yui.github.io/yuicompressor/) and [Google Closure Compiler](https://github.com/google/closure-compiler) for JS. As most existing implementations either use JavaScript, use regexes, and don't focus on performance, they are pretty slow. + +This minifier proves to be that fast and extensive minifier that can handle HTML and any other filetype it may contain (CSS, JS, ...). It is usually orders of magnitude faster than existing minifiers. + +## Installation +Run the following command + + go get -u github.com/tdewolff/minify/v2 + +or add the following imports and run the project with `go get` +``` go +import ( + "github.com/tdewolff/minify/v2" + "github.com/tdewolff/minify/v2/css" + "github.com/tdewolff/minify/v2/html" + "github.com/tdewolff/minify/v2/js" + "github.com/tdewolff/minify/v2/json" + "github.com/tdewolff/minify/v2/svg" + "github.com/tdewolff/minify/v2/xml" +) +``` + +## API stability +There is no guarantee for absolute stability, but I take issues and bugs seriously and don't take API changes lightly. The library will be maintained in a compatible way unless vital bugs prevent me from doing so. There has been one API change after v1 which added options support and I took the opportunity to push through some more API clean up as well. There are no plans whatsoever for future API changes. + +## Testing +For all subpackages and the imported `parse` and `buffer` packages, test coverage of 100% is pursued. Besides full coverage, the minifiers are [fuzz tested](https://github.com/tdewolff/fuzz) using [github.com/dvyukov/go-fuzz](http://www.github.com/dvyukov/go-fuzz), see [the wiki](https://github.com/tdewolff/minify/wiki) for the most important bugs found by fuzz testing. Furthermore am I working on adding visual testing to ensure that minification doesn't change anything visually. By using the WebKit browser to render the original and minified pages we can check whether any pixel is different. + +These tests ensure that everything works as intended, the code does not crash (whatever the input) and that it doesn't change the final result visually. If you still encounter a bug, please report [here](https://github.com/tdewolff/minify/issues)! + +## Performance +The benchmarks directory contains a number of standardized samples used to compare performance between changes. To give an indication of the speed of this library, I've ran the tests on my Thinkpad T460 (i5-6300U quad-core 2.4GHz running Arch Linux) using Go 1.9.2. + +``` +name time/op +CSS/sample_bootstrap.css-4 2.26ms ± 0% +CSS/sample_gumby.css-4 2.92ms ± 1% +HTML/sample_amazon.html-4 2.33ms ± 2% +HTML/sample_bbc.html-4 1.02ms ± 1% +HTML/sample_blogpost.html-4 171µs ± 2% +HTML/sample_es6.html-4 14.5ms ± 0% +HTML/sample_stackoverflow.html-4 2.41ms ± 1% +HTML/sample_wikipedia.html-4 4.76ms ± 0% +JS/sample_ace.js-4 7.41ms ± 0% +JS/sample_dot.js-4 63.7µs ± 0% +JS/sample_jquery.js-4 2.99ms ± 0% +JS/sample_jqueryui.js-4 5.92ms ± 2% +JS/sample_moment.js-4 1.09ms ± 1% +JSON/sample_large.json-4 2.95ms ± 0% +JSON/sample_testsuite.json-4 1.51ms ± 1% +JSON/sample_twitter.json-4 6.75µs ± 1% +SVG/sample_arctic.svg-4 62.3ms ± 1% +SVG/sample_gopher.svg-4 218µs ± 0% +SVG/sample_usa.svg-4 33.1ms ± 3% +XML/sample_books.xml-4 36.2µs ± 0% +XML/sample_catalog.xml-4 14.9µs ± 0% +XML/sample_omg.xml-4 6.31ms ± 1% + +name speed +CSS/sample_bootstrap.css-4 60.8MB/s ± 0% +CSS/sample_gumby.css-4 63.9MB/s ± 1% +HTML/sample_amazon.html-4 203MB/s ± 2% +HTML/sample_bbc.html-4 113MB/s ± 1% +HTML/sample_blogpost.html-4 123MB/s ± 2% +HTML/sample_es6.html-4 70.7MB/s ± 0% +HTML/sample_stackoverflow.html-4 85.2MB/s ± 1% +HTML/sample_wikipedia.html-4 93.6MB/s ± 0% +JS/sample_ace.js-4 86.9MB/s ± 0% +JS/sample_dot.js-4 81.0MB/s ± 0% +JS/sample_jquery.js-4 82.8MB/s ± 0% +JS/sample_jqueryui.js-4 79.3MB/s ± 2% +JS/sample_moment.js-4 91.2MB/s ± 1% +JSON/sample_large.json-4 258MB/s ± 0% +JSON/sample_testsuite.json-4 457MB/s ± 1% +JSON/sample_twitter.json-4 226MB/s ± 1% +SVG/sample_arctic.svg-4 23.6MB/s ± 1% +SVG/sample_gopher.svg-4 26.7MB/s ± 0% +SVG/sample_usa.svg-4 30.9MB/s ± 3% +XML/sample_books.xml-4 122MB/s ± 0% +XML/sample_catalog.xml-4 130MB/s ± 0% +XML/sample_omg.xml-4 180MB/s ± 1% +``` + +## HTML + +HTML (with JS and CSS) minification typically shaves off about 10%. + +The HTML5 minifier uses these minifications: + +- strip unnecessary whitespace and otherwise collapse it to one space (or newline if it originally contained a newline) +- strip superfluous quotes, or uses single/double quotes whichever requires fewer escapes +- strip default attribute values and attribute boolean values +- strip some empty attributes +- strip unrequired tags (`html`, `head`, `body`, ...) +- strip unrequired end tags (`tr`, `td`, `li`, ... and often `p`) +- strip default protocols (`http:`, `https:` and `javascript:`) +- strip all comments (including conditional comments, old IE versions are not supported anymore by Microsoft) +- shorten `doctype` and `meta` charset +- lowercase tags, attributes and some values to enhance gzip compression + +Options: + +- `KeepConditionalComments` preserve all IE conditional comments such as `` and ``, see https://msdn.microsoft.com/en-us/library/ms537512(v=vs.85).aspx#syntax +- `KeepDefaultAttrVals` preserve default attribute values such as `