/* Minify middleware is simple and generic using 'tdewolff/minify' package. Middleware minifies HTML, Inline CSS and Inline JS. Compatible with Goji, Gorilla, Gin & net/http (amongst many others). Refer: https://github.com/jeevatkm/middleware/examples */ func Minify(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { mw := &minifyWriter{ ResponseWriter: w, Body: &bytes.Buffer{}, } h.ServeHTTP(mw, r) hdr := w.Header() ct := hdr.Get("Content-Type") if mediaType.MatchString(ct) { rb, err := minify.Bytes(minifier, ct, mw.Body.Bytes()) if err != nil { _ = err // unsupported mediatype error or internal } hdr.Del("Content-Length") hdr.Set("Content-Length", strconv.Itoa(len(rb))) w.Write(rb) } else { w.Write(mw.Body.Bytes()) } }) }
func createAppMinJsFile(appScripts []string, dir string) { appMinJsWrtr, err := os.Create(filepath.Join(dir, "app.min.js")) if err != nil { log.Fatal(err) } defer closeFile(appMinJsWrtr, true) var buffer bytes.Buffer for _, script := range appScripts { file, err := os.Open(filepath.Join(dir, script)) if err != nil { log.Fatal(err) } defer closeFile(file, false) _, err = io.Copy(&buffer, file) if err != nil { log.Fatalf("Error copying script file '%v' to buffer: %v", script, err) } } mimetype := "text/javascript" minifier := minify.New() minifier.AddFunc(mimetype, js.Minify) minified, err := minify.Bytes(minifier, mimetype, buffer.Bytes()) if err != nil { log.Fatalf("Error during minification: %v", err) } _, err = io.Copy(appMinJsWrtr, bytes.NewReader(minified)) if err != nil { log.Fatalf("Error copying to file app.min.js: %v", err) } }
func svg(path string, info os.FileInfo) error { input, err := ioutil.ReadFile(path) if err != nil { return err } min, err := minify.Bytes(minifier, "image/svg+xml", input) if err != nil { return fmt.Errorf("cannot minify svg: %v", err) } dst := filepath.Join(outputDir, path) if err := writeFile(dst, min); err != nil { return err } return nil }
func graphvizDot(path string, info os.FileInfo) error { f, err := os.Open(path) if err != nil { return err } defer f.Close() cmd := exec.Command("dot", "-Tsvg") cmd.Stdin = f buf, err := cmd.Output() if err != nil { return fmt.Errorf("error running dot: %v", err) } min, err := minify.Bytes(minifier, "image/svg+xml", buf) if err != nil { return fmt.Errorf("cannot minify svg: %v", err) } dst := filepath.Join(outputDir, strings.TrimSuffix(path, ".dot")+".svg") if err := writeFile(dst, min); err != nil { return err } return nil }
func markdown(path string, info os.FileInfo) error { input, err := ioutil.ReadFile(path) if err != nil { return err } // extract the header out of the markdown, so we can control the // layout better; blackfriday would put the toc above the h1, and // include the singular h1 in the toc, causing stutter. idx := bytes.IndexByte(input, '\n') if idx == -1 { return errors.New("markdown has no content") } titleMD, input := input[:idx], input[idx+1:] htmlFlags := (0 | blackfriday.HTML_USE_SMARTYPANTS | blackfriday.HTML_SMARTYPANTS_FRACTIONS | blackfriday.HTML_SMARTYPANTS_LATEX_DASHES | blackfriday.HTML_USE_XHTML | blackfriday.HTML_FOOTNOTE_RETURN_LINKS | 0) // HtmlRenderer demands a title and a css path here, but we only // render a fragment so those are not used renderer := blackfriday.HtmlRenderer(htmlFlags, "", "") extensions := (0 | blackfriday.EXTENSION_NO_INTRA_EMPHASIS | blackfriday.EXTENSION_TABLES | blackfriday.EXTENSION_FENCED_CODE | blackfriday.EXTENSION_AUTOLINK | blackfriday.EXTENSION_STRIKETHROUGH | blackfriday.EXTENSION_SPACE_HEADERS | blackfriday.EXTENSION_FOOTNOTES | blackfriday.EXTENSION_HEADER_IDS | blackfriday.EXTENSION_AUTO_HEADER_IDS | 0) titleHTML := blackfriday.Markdown(titleMD, renderer, extensions) contentHTML := blackfriday.Markdown(input, renderer, extensions) tocFlags := htmlFlags | blackfriday.HTML_TOC | blackfriday.HTML_OMIT_CONTENTS tocRenderer := blackfriday.HtmlRenderer(tocFlags, "", "") tocHTML := blackfriday.Markdown(input, tocRenderer, extensions) body := &html.Node{ Type: html.ElementNode, Data: "body", DataAtom: atom.Body, } nodes, err := html.ParseFragment(bytes.NewReader(titleHTML), body) if err != nil { return fmt.Errorf("cannot parse generated html: %v", err) } if len(nodes) == 0 || nodes[0].Type != html.ElementNode || nodes[0].DataAtom != atom.H1 { return errors.New("markdown does not start with a header") } title := childText(nodes[0]) var buf bytes.Buffer prettyPath := "/" + strings.TrimSuffix(path, ".md") if dir, file := filepath.Split(prettyPath); file == "index" { prettyPath = dir } data := struct { Path string Title string H1 template.HTML TOC template.HTML Content template.HTML }{ Path: prettyPath, Title: title, H1: template.HTML(titleHTML), TOC: template.HTML(tocHTML), Content: template.HTML(contentHTML), } if err := layout.Execute(&buf, data); err != nil { return fmt.Errorf("executing template: %v", err) } min, err := minify.Bytes(minifier, "text/html", buf.Bytes()) if err != nil { return fmt.Errorf("cannot minify html: %v", err) } dst := filepath.Join(outputDir, strings.TrimSuffix(path, ".md")+".html") if err := writeFile(dst, min); err != nil { return err } return nil }