github.com/anakojm/hugo-katex@v0.0.0-20231023141351-42d6f5de9c0b/transform/livereloadinject/livereloadinject.go (about) 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/common/loggers" 24 25 "github.com/gohugoio/hugo/transform" 26 ) 27 28 const warnMessage = `"head" or "body" tag is required in html to append livereload script. ` + 29 "As a fallback, Hugo injects it somewhere but it might not work properly." 30 31 var warnScript = fmt.Sprintf(`<script data-no-instant defer>console.warn('%s');</script>`, warnMessage) 32 33 type tag struct { 34 markup []byte 35 appendScript bool 36 warnRequired bool 37 } 38 39 var tags = []tag{ 40 {markup: []byte("<head"), appendScript: true}, 41 {markup: []byte("<HEAD"), appendScript: true}, 42 {markup: []byte("</body>")}, 43 {markup: []byte("</BODY>")}, 44 {markup: []byte("<html"), appendScript: true, warnRequired: true}, 45 {markup: []byte("<HTML"), appendScript: true, warnRequired: true}, 46 } 47 48 // New creates a function that can be used 49 // to inject a script tag for the livereload JavaScript in a HTML document. 50 func New(baseURL url.URL) transform.Transformer { 51 return func(ft transform.FromTo) error { 52 b := ft.From().Bytes() 53 idx := -1 54 var match tag 55 // We used to insert the livereload script right before the closing body. 56 // This does not work when combined with tools such as Turbolinks. 57 // So we try to inject the script as early as possible. 58 for _, t := range tags { 59 idx = bytes.Index(b, t.markup) 60 if idx != -1 { 61 match = t 62 break 63 } 64 } 65 66 path := strings.TrimSuffix(baseURL.Path, "/") 67 68 src := path + "/livereload.js?mindelay=10&v=2" 69 src += "&port=" + baseURL.Port() 70 src += "&path=" + strings.TrimPrefix(path+"/livereload", "/") 71 72 c := make([]byte, len(b)) 73 copy(c, b) 74 75 if idx == -1 { 76 idx = len(b) 77 match = tag{warnRequired: true} 78 } 79 80 script := []byte(fmt.Sprintf(`<script src="%s" data-no-instant defer></script>`, html.EscapeString(src))) 81 82 i := idx 83 if match.appendScript { 84 i += bytes.Index(b[i:], []byte(">")) + 1 85 } 86 87 if match.warnRequired { 88 script = append(script, []byte(warnScript)...) 89 } 90 91 c = append(c[:i], append(script, c[i:]...)...) 92 93 if _, err := ft.To().Write(c); err != nil { 94 loggers.Log().Warnf("Failed to inject LiveReload script:", err) 95 } 96 return nil 97 } 98 }