livereloadinject.go (2268B)
1 // Copyright 2018 The Hugo Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 package livereloadinject
15
16 import (
17 "bytes"
18 "fmt"
19 "html"
20 "net/url"
21 "strings"
22
23 "github.com/gohugoio/hugo/helpers"
24 "github.com/gohugoio/hugo/transform"
25 )
26
27 type tag struct {
28 markup []byte
29 appendScript bool
30 }
31
32 var tags = []tag{
33 {markup: []byte("<head>"), appendScript: true},
34 {markup: []byte("<HEAD>"), appendScript: true},
35 {markup: []byte("</body>")},
36 {markup: []byte("</BODY>")},
37 }
38
39 // New creates a function that can be used
40 // to inject a script tag for the livereload JavaScript in a HTML document.
41 func New(baseURL url.URL) transform.Transformer {
42 return func(ft transform.FromTo) error {
43 b := ft.From().Bytes()
44 idx := -1
45 var match tag
46 // We used to insert the livereload script right before the closing body.
47 // This does not work when combined with tools such as Turbolinks.
48 // So we try to inject the script as early as possible.
49 for _, t := range tags {
50 idx = bytes.Index(b, t.markup)
51 if idx != -1 {
52 match = t
53 break
54 }
55 }
56
57 path := strings.TrimSuffix(baseURL.Path, "/")
58
59 src := path + "/livereload.js?mindelay=10&v=2"
60 src += "&port=" + baseURL.Port()
61 src += "&path=" + strings.TrimPrefix(path+"/livereload", "/")
62
63 c := make([]byte, len(b))
64 copy(c, b)
65
66 if idx == -1 {
67 _, err := ft.To().Write(c)
68 return err
69 }
70
71 script := []byte(fmt.Sprintf(`<script src="%s" data-no-instant defer></script>`, html.EscapeString(src)))
72
73 i := idx
74 if match.appendScript {
75 i += len(match.markup)
76 }
77
78 c = append(c[:i], append(script, c[i:]...)...)
79
80 if _, err := ft.To().Write(c); err != nil {
81 helpers.DistinctWarnLog.Println("Failed to inject LiveReload script:", err)
82 }
83 return nil
84 }
85 }