Skip to content

Commit

Permalink
Merge pull request #894 from macrat/hoisting-html
Browse files Browse the repository at this point in the history
Minify HTML more
  • Loading branch information
macrat committed Feb 8, 2024
2 parents 9460e66 + d8d6131 commit ff78757
Show file tree
Hide file tree
Showing 5 changed files with 339 additions and 27 deletions.
3 changes: 2 additions & 1 deletion builder/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ require (
github.com/dsoprea/go-jpeg-image-structure/v2 v2.0.0-20221012074422-4f3f7e934102
github.com/fsnotify/fsnotify v1.7.0
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/google/go-cmp v0.6.0
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/tdewolff/minify/v2 v2.20.14
github.com/yuin/goldmark v1.6.0
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
golang.org/x/image v0.15.0
golang.org/x/net v0.20.0
gopkg.in/yaml.v3 v3.0.1
)

Expand All @@ -27,7 +29,6 @@ require (
github.com/go-xmlfmt/xmlfmt v1.1.2 // indirect
github.com/golang/geo v0.0.0-20230421003525-6adc56603217 // indirect
github.com/tdewolff/parse/v2 v2.7.9 // indirect
golang.org/x/net v0.20.0 // indirect
golang.org/x/sys v0.16.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
2 changes: 2 additions & 0 deletions builder/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFig
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
github.com/golang/geo v0.0.0-20230421003525-6adc56603217 h1:HKlyj6in2JV6wVkmQ4XmG/EIm+SCYlPZ+V4GWit7Z+I=
github.com/golang/geo v0.0.0-20230421003525-6adc56603217/go.mod h1:8wI0hitZ3a1IxZfeH3/5I97CI8i5cLGsYe7xNhQGs9U=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand Down
13 changes: 4 additions & 9 deletions builder/markdown.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package main

import (
"bytes"
"encoding/json"
"fmt"
"html/template"
"io"
"net/url"
"regexp"
Expand Down Expand Up @@ -137,12 +137,7 @@ func (r *CodeRenderer) Render(w markdown.BufWriter, source []byte, node ast.Node
return ast.WalkStop, err
}

jsonCode, err := json.Marshal(plainCode)
if err != nil {
return ast.WalkStop, err
}

_, err = fmt.Fprintf(w, `<div class="chroma"><button onclick='copyCodeBlock(this, %s)'>Copy</button><pre>`, jsonCode)
_, err = fmt.Fprintf(w, `<div class="chroma" data-code="%s"><button onclick='copyCodeBlock(this)'>Copy</button><pre>`, template.HTMLEscapeString(plainCode))
if err != nil {
return ast.WalkStop, err
}
Expand All @@ -159,8 +154,8 @@ func (r *CodeRenderer) Render(w markdown.BufWriter, source []byte, node ast.Node
func (r *CodeRenderer) WriteCodeBlockAssets(w io.Writer) error {
_, err := fmt.Fprintf(w, `
<script>
function copyCodeBlock(elm, code) {
navigator.clipboard.writeText(code).then(() => {
function copyCodeBlock(elm) {
navigator.clipboard.writeText(elm.parentNode.dataset.code).then(() => {
elm.innerText = "Copied!";
setTimeout(() => {
elm.innerText = "Copy";
Expand Down
162 changes: 145 additions & 17 deletions builder/minify.go
Original file line number Diff line number Diff line change
@@ -1,39 +1,52 @@
package main

import (
"bytes"
"io"
"strings"

"github.com/tdewolff/minify/v2"
"github.com/tdewolff/minify/v2/css"
"github.com/tdewolff/minify/v2/html"
mhtml "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"
"golang.org/x/net/html"
"golang.org/x/net/html/atom"
)

var (
Minifier = minify.New()
MinifierFuncs = map[string]minify.MinifierFunc{
"text/html": mhtml.Minify,
"text/css": css.Minify,
"text/javascript": js.Minify,
"application/javascript": js.Minify,
"application/json": json.Minify,
"application/ld+json": json.Minify,
"image/svg+xml": svg.Minify,
"text/xml": xml.Minify,
"application/xml": xml.Minify,
}
)

func init() {
for k, v := range MinifierFuncs {
Minifier.AddFunc(k, v)
}
}

func MinifyWriter(mimetype string, w io.Writer) io.WriteCloser {
types := map[string]minify.MinifierFunc{
"text/html": html.Minify,
"text/css": css.Minify,
"text/javascript": js.Minify,
"application/json": json.Minify,
"application/ld+json": json.Minify,
"image/svg+xml": svg.Minify,
"text/xml": xml.Minify,
"application/xml": xml.Minify,
}

if _, ok := types[mimetype]; !ok {
if _, ok := MinifierFuncs[mimetype]; !ok {
return NopWriteCloser{w}
}

m := minify.New()
for k, v := range types {
m.AddFunc(k, v)
if mimetype == "text/html" {
return &HTMLMinifier{w: Minifier.Writer(mimetype, w)}
}

return m.Writer(mimetype, w)
return Minifier.Writer(mimetype, w)
}

type NopWriteCloser struct {
Expand All @@ -47,3 +60,118 @@ func (w NopWriteCloser) Write(p []byte) (int, error) {
func (w NopWriteCloser) Close() error {
return nil
}

type HTMLMinifier struct {
w io.WriteCloser
buf bytes.Buffer
}

func (w *HTMLMinifier) Write(p []byte) (int, error) {
return w.buf.Write(p)
}

func (w *HTMLMinifier) Close() error {
if err := HoistingHTML(w.w, &w.buf); err != nil {
return err
}
return w.w.Close()
}

func getAttribute(n *html.Node, key, default_ string) string {
for _, attr := range n.Attr {
if attr.Key == key {
return attr.Val
}
}
return default_
}

func nextNode(n *html.Node) *html.Node {
if n.FirstChild != nil {
return n.FirstChild
} else if n.NextSibling != nil {
return n.NextSibling
} else {
for {
n = n.Parent
if n == nil {
break
}
if n.NextSibling != nil {
return n.NextSibling
}
}
}
return nil
}

func HoistingHTML(w io.Writer, r io.Reader) error {
var scripts []string
var styles []string

root, err := html.Parse(r)
if err != nil {
return err
}

node := root
var head, body *html.Node

popAndGoNext := func() {
var next *html.Node
for next == nil {
next = node.NextSibling
if next == nil {
node = node.Parent
}
}

node.Parent.RemoveChild(node)
node = next
}

for node != nil {
if node.DataAtom == atom.Head {
head = node
}
if node.DataAtom == atom.Body {
body = node
}

if node.DataAtom == atom.Script && getAttribute(node, "type", "text/javascript") == "text/javascript" && node.FirstChild != nil {
scripts = append(scripts, node.FirstChild.Data)
popAndGoNext()
} else if node.DataAtom == atom.Style && getAttribute(node, "type", "text/css") == "text/css" && node.FirstChild != nil {
styles = append(styles, node.FirstChild.Data)
popAndGoNext()
} else {
node = nextNode(node)
}
}

if script := strings.Join(scripts, "\n;\n"); script != "" {
body.AppendChild(&html.Node{
Type: html.ElementNode,
DataAtom: atom.Script,
Data: "script",
FirstChild: &html.Node{
Type: html.TextNode,
Data: script,
},
})
}

if style := strings.Join(styles, "\n"); style != "" {
head.AppendChild(&html.Node{
Type: html.ElementNode,
DataAtom: atom.Style,
Data: "style",
FirstChild: &html.Node{
Type: html.TextNode,
Data: style,
},
})
}

return html.Render(w, root)
}

0 comments on commit ff78757

Please sign in to comment.