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 }