Skip to content

Commit 8736244

Browse files
committed
transform/livereloadinject: Inject livereload script right after head if possible
This makes it work with Turbolinks. Fixes gohugoio#6821
1 parent d8e6851 commit 8736244

File tree

2 files changed

+71
-24
lines changed

2 files changed

+71
-24
lines changed

Diff for: transform/livereloadinject/livereloadinject.go

+41-12
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,54 @@ import (
2121
"github.com/gohugoio/hugo/transform"
2222
)
2323

24+
type tag struct {
25+
markup []byte
26+
appendScript bool
27+
}
28+
29+
var tags = []tag{
30+
tag{markup: []byte("<head>"), appendScript: true},
31+
tag{markup: []byte("<HEAD>"), appendScript: true},
32+
tag{markup: []byte("</body>")},
33+
tag{markup: []byte("</BODY>")},
34+
}
35+
2436
// New creates a function that can be used
2537
// to inject a script tag for the livereload JavaScript in a HTML document.
2638
func New(port int) transform.Transformer {
2739
return func(ft transform.FromTo) error {
2840
b := ft.From().Bytes()
29-
endBodyTag := "</body>"
30-
match := []byte(endBodyTag)
31-
replaceTemplate := `<script data-no-instant>document.write('<script src="/livereload.js?port=%d&mindelay=10&v=2"></' + 'script>')</script>%s`
32-
replace := []byte(fmt.Sprintf(replaceTemplate, port, endBodyTag))
33-
34-
newcontent := bytes.Replace(b, match, replace, 1)
35-
if len(newcontent) == len(b) {
36-
endBodyTag = "</BODY>"
37-
replace := []byte(fmt.Sprintf(replaceTemplate, port, endBodyTag))
38-
match := []byte(endBodyTag)
39-
newcontent = bytes.Replace(b, match, replace, 1)
41+
var idx = -1
42+
var match tag
43+
// We used to insert the livereload script right before the closing body.
44+
// This does not work when combined with tools such as Turbolinks.
45+
// So we try to inject the script as early as possible.
46+
for _, t := range tags {
47+
idx = bytes.Index(b, t.markup)
48+
if idx != -1 {
49+
match = t
50+
break
51+
}
4052
}
4153

42-
if _, err := ft.To().Write(newcontent); err != nil {
54+
c := make([]byte, len(b))
55+
copy(c, b)
56+
57+
if idx == -1 {
58+
_, err := ft.To().Write(c)
59+
return err
60+
}
61+
62+
script := []byte(fmt.Sprintf(`<script data-no-instant>document.write('<script src="/livereload.js?port=%d&mindelay=10&v=2"></' + 'script>')</script>`, port))
63+
64+
i := idx
65+
if match.appendScript {
66+
i += len(match.markup)
67+
}
68+
69+
c = append(c[:i], append(script, c[i:]...)...)
70+
71+
if _, err := ft.To().Write(c); err != nil {
4372
helpers.DistinctWarnLog.Println("Failed to inject LiveReload script:", err)
4473
}
4574
return nil

Diff for: transform/livereloadinject/livereloadinject_test.go

+30-12
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,45 @@ package livereloadinject
1515

1616
import (
1717
"bytes"
18-
"fmt"
1918
"strings"
2019
"testing"
2120

21+
qt "github.com/frankban/quicktest"
2222
"github.com/gohugoio/hugo/transform"
2323
)
2424

2525
func TestLiveReloadInject(t *testing.T) {
26-
doTestLiveReloadInject(t, "</body>")
27-
doTestLiveReloadInject(t, "</BODY>")
28-
}
26+
c := qt.New(t)
2927

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

34-
tr := transform.New(New(1313))
35-
tr.Apply(out, in)
33+
tr := transform.New(New(1313))
34+
tr.Apply(out, in)
3635

37-
expected := fmt.Sprintf(`<script data-no-instant>document.write('<script src="/livereload.js?port=1313&mindelay=10&v=2"></' + 'script>')</script>%s`, bodyEndTag)
38-
if out.String() != expected {
39-
t.Errorf("Expected %s got %s", expected, out.String())
36+
return out.String()
4037
}
38+
39+
c.Run("Head lower", func(c *qt.C) {
40+
c.Assert(apply("<html><head>foo"), qt.Equals, "<html><head>"+expectBase+"foo")
41+
})
42+
43+
c.Run("Head upper", func(c *qt.C) {
44+
c.Assert(apply("<html><HEAD>foo"), qt.Equals, "<html><HEAD>"+expectBase+"foo")
45+
})
46+
47+
c.Run("Body lower", func(c *qt.C) {
48+
c.Assert(apply("foo</body>"), qt.Equals, "foo"+expectBase+"</body>")
49+
})
50+
51+
c.Run("Body upper", func(c *qt.C) {
52+
c.Assert(apply("foo</BODY>"), qt.Equals, "foo"+expectBase+"</BODY>")
53+
})
54+
55+
c.Run("No match", func(c *qt.C) {
56+
c.Assert(apply("<h1>No match</h1>"), qt.Equals, "<h1>No match</h1>")
57+
})
58+
4159
}

0 commit comments

Comments
 (0)