Skip to content

Commit

Permalink
transform/livereloadinject: Inject livereload script right after head…
Browse files Browse the repository at this point in the history
… if possible

We used to insert the livereload script right before the closing body.

This dord  not work when combined with tools such as Turbolinks.

This commit changes it So we try to inject the script as early as possible.

Fixes gohugoio#6821
  • Loading branch information
bep committed Jan 29, 2020
1 parent d8e6851 commit d694d02
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 24 deletions.
53 changes: 41 additions & 12 deletions transform/livereloadinject/livereloadinject.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,54 @@ import (
"github.com/gohugoio/hugo/transform"
)

type tag struct {
markup []byte
appendScript bool
}

var tags = []tag{
tag{markup: []byte("<head>"), appendScript: true},
tag{markup: []byte("<HEAD>"), appendScript: true},
tag{markup: []byte("</body>")},
tag{markup: []byte("</BODY>")},
}

// New creates a function that can be used
// to inject a script tag for the livereload JavaScript in a HTML document.
func New(port int) transform.Transformer {
return func(ft transform.FromTo) error {
b := ft.From().Bytes()
endBodyTag := "</body>"
match := []byte(endBodyTag)
replaceTemplate := `<script data-no-instant>document.write('<script src="/livereload.js?port=%d&mindelay=10&v=2"></' + 'script>')</script>%s`
replace := []byte(fmt.Sprintf(replaceTemplate, port, endBodyTag))

newcontent := bytes.Replace(b, match, replace, 1)
if len(newcontent) == len(b) {
endBodyTag = "</BODY>"
replace := []byte(fmt.Sprintf(replaceTemplate, port, endBodyTag))
match := []byte(endBodyTag)
newcontent = bytes.Replace(b, match, replace, 1)
var idx = -1
var match tag
// We used to insert the livereload script right before the closing body.
// This does not work when combined with tools such as Turbolinks.
// So we try to inject the script as early as possible.
for _, t := range tags {
idx = bytes.Index(b, t.markup)
if idx != -1 {
match = t
break
}
}

if _, err := ft.To().Write(newcontent); err != nil {
c := make([]byte, len(b))
copy(c, b)

if idx == -1 {
_, err := ft.To().Write(c)
return err
}

script := []byte(fmt.Sprintf(`<script data-no-instant>document.write('<script src="/livereload.js?port=%d&mindelay=10&v=2"></' + 'script>')</script>`, port))

i := idx
if match.appendScript {
i += len(match.markup)
}

c = append(c[:i], append(script, c[i:]...)...)

if _, err := ft.To().Write(c); err != nil {
helpers.DistinctWarnLog.Println("Failed to inject LiveReload script:", err)
}
return nil
Expand Down
42 changes: 30 additions & 12 deletions transform/livereloadinject/livereloadinject_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,45 @@ package livereloadinject

import (
"bytes"
"fmt"
"strings"
"testing"

qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/transform"
)

func TestLiveReloadInject(t *testing.T) {
doTestLiveReloadInject(t, "</body>")
doTestLiveReloadInject(t, "</BODY>")
}
c := qt.New(t)

func doTestLiveReloadInject(t *testing.T, bodyEndTag string) {
out := new(bytes.Buffer)
in := strings.NewReader(bodyEndTag)
expectBase := `<script data-no-instant>document.write('<script src="/livereload.js?port=1313&mindelay=10&v=2"></' + 'script>')</script>`
apply := func(s string) string {
out := new(bytes.Buffer)
in := strings.NewReader(s)

tr := transform.New(New(1313))
tr.Apply(out, in)
tr := transform.New(New(1313))
tr.Apply(out, in)

expected := fmt.Sprintf(`<script data-no-instant>document.write('<script src="/livereload.js?port=1313&mindelay=10&v=2"></' + 'script>')</script>%s`, bodyEndTag)
if out.String() != expected {
t.Errorf("Expected %s got %s", expected, out.String())
return out.String()
}

c.Run("Head lower", func(c *qt.C) {
c.Assert(apply("<html><head>foo"), qt.Equals, "<html><head>"+expectBase+"foo")
})

c.Run("Head upper", func(c *qt.C) {
c.Assert(apply("<html><HEAD>foo"), qt.Equals, "<html><HEAD>"+expectBase+"foo")
})

c.Run("Body lower", func(c *qt.C) {
c.Assert(apply("foo</body>"), qt.Equals, "foo"+expectBase+"</body>")
})

c.Run("Body upper", func(c *qt.C) {
c.Assert(apply("foo</BODY>"), qt.Equals, "foo"+expectBase+"</BODY>")
})

c.Run("No match", func(c *qt.C) {
c.Assert(apply("<h1>No match</h1>"), qt.Equals, "<h1>No match</h1>")
})

}

0 comments on commit d694d02

Please sign in to comment.