mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-07 20:30:36 -05:00
Proper integration of live reload with automatic injection
This commit is contained in:
parent
60ed5bda2b
commit
be1ee22032
8 changed files with 224 additions and 121 deletions
|
@ -15,6 +15,7 @@ package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -26,6 +27,7 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/hugo/helpers"
|
"github.com/spf13/hugo/helpers"
|
||||||
"github.com/spf13/hugo/hugolib"
|
"github.com/spf13/hugo/hugolib"
|
||||||
|
"github.com/spf13/hugo/livereload"
|
||||||
"github.com/spf13/hugo/utils"
|
"github.com/spf13/hugo/utils"
|
||||||
"github.com/spf13/hugo/watcher"
|
"github.com/spf13/hugo/watcher"
|
||||||
jww "github.com/spf13/jwalterweatherman"
|
jww "github.com/spf13/jwalterweatherman"
|
||||||
|
@ -46,6 +48,7 @@ Complete documentation is available at http://hugo.spf13.com`,
|
||||||
build()
|
build()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var hugoCmdV *cobra.Command
|
var hugoCmdV *cobra.Command
|
||||||
|
|
||||||
var BuildWatch, Draft, UglyUrls, Verbose, Logging, VerboseLog, DisableRSS, DisableSitemap bool
|
var BuildWatch, Draft, UglyUrls, Verbose, Logging, VerboseLog, DisableRSS, DisableSitemap bool
|
||||||
|
@ -66,19 +69,19 @@ func AddCommands() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
HugoCmd.PersistentFlags().BoolVarP(&Draft, "build-drafts", "D", false, "include content marked as draft")
|
HugoCmd.PersistentFlags().BoolVarP(&Draft, "buildDrafts", "D", false, "include content marked as draft")
|
||||||
HugoCmd.PersistentFlags().BoolVar(&DisableRSS, "disableRSS", false, "Do not build RSS files")
|
HugoCmd.PersistentFlags().BoolVar(&DisableRSS, "disableRSS", false, "Do not build RSS files")
|
||||||
HugoCmd.PersistentFlags().BoolVar(&DisableSitemap, "disableSitemap", false, "Do not build Sitemap file")
|
HugoCmd.PersistentFlags().BoolVar(&DisableSitemap, "disableSitemap", false, "Do not build Sitemap file")
|
||||||
HugoCmd.PersistentFlags().StringVarP(&Source, "source", "s", "", "filesystem path to read files relative from")
|
HugoCmd.PersistentFlags().StringVarP(&Source, "source", "s", "", "filesystem path to read files relative from")
|
||||||
HugoCmd.PersistentFlags().StringVarP(&Destination, "destination", "d", "", "filesystem path to write files to")
|
HugoCmd.PersistentFlags().StringVarP(&Destination, "destination", "d", "", "filesystem path to write files to")
|
||||||
HugoCmd.PersistentFlags().StringVarP(&Theme, "theme", "t", "", "theme to use (located in /themes/THEMENAME/)")
|
HugoCmd.PersistentFlags().StringVarP(&Theme, "theme", "t", "", "theme to use (located in /themes/THEMENAME/)")
|
||||||
HugoCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")
|
HugoCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")
|
||||||
HugoCmd.PersistentFlags().BoolVar(&UglyUrls, "uglyurls", false, "if true, use /filename.html instead of /filename/")
|
HugoCmd.PersistentFlags().BoolVar(&UglyUrls, "uglyUrls", false, "if true, use /filename.html instead of /filename/")
|
||||||
HugoCmd.PersistentFlags().StringVarP(&BaseUrl, "base-url", "b", "", "hostname (and path) to the root eg. http://spf13.com/")
|
HugoCmd.PersistentFlags().StringVarP(&BaseUrl, "baseUrl", "b", "", "hostname (and path) to the root eg. http://spf13.com/")
|
||||||
HugoCmd.PersistentFlags().StringVar(&CfgFile, "config", "", "config file (default is path/config.yaml|json|toml)")
|
HugoCmd.PersistentFlags().StringVar(&CfgFile, "config", "", "config file (default is path/config.yaml|json|toml)")
|
||||||
HugoCmd.PersistentFlags().BoolVar(&Logging, "log", false, "Enable Logging")
|
HugoCmd.PersistentFlags().BoolVar(&Logging, "log", false, "Enable Logging")
|
||||||
HugoCmd.PersistentFlags().StringVar(&LogFile, "logfile", "", "Log File path (if set, logging enabled automatically)")
|
HugoCmd.PersistentFlags().StringVar(&LogFile, "logFile", "", "Log File path (if set, logging enabled automatically)")
|
||||||
HugoCmd.PersistentFlags().BoolVar(&VerboseLog, "verboselog", false, "verbose logging")
|
HugoCmd.PersistentFlags().BoolVar(&VerboseLog, "verboseLog", false, "verbose logging")
|
||||||
HugoCmd.PersistentFlags().BoolVar(&nitro.AnalysisOn, "stepAnalysis", false, "display memory and timing of different steps of the program")
|
HugoCmd.PersistentFlags().BoolVar(&nitro.AnalysisOn, "stepAnalysis", false, "display memory and timing of different steps of the program")
|
||||||
HugoCmd.Flags().BoolVarP(&BuildWatch, "watch", "w", false, "watch filesystem for changes and recreate as needed")
|
HugoCmd.Flags().BoolVarP(&BuildWatch, "watch", "w", false, "watch filesystem for changes and recreate as needed")
|
||||||
hugoCmdV = HugoCmd
|
hugoCmdV = HugoCmd
|
||||||
|
@ -94,6 +97,7 @@ func InitializeConfig() {
|
||||||
|
|
||||||
viper.RegisterAlias("taxonomies", "indexes")
|
viper.RegisterAlias("taxonomies", "indexes")
|
||||||
|
|
||||||
|
viper.SetDefault("Watch", false)
|
||||||
viper.SetDefault("MetadataFormat", "toml")
|
viper.SetDefault("MetadataFormat", "toml")
|
||||||
viper.SetDefault("DisableRSS", false)
|
viper.SetDefault("DisableRSS", false)
|
||||||
viper.SetDefault("DisableSitemap", false)
|
viper.SetDefault("DisableSitemap", false)
|
||||||
|
@ -112,12 +116,13 @@ func InitializeConfig() {
|
||||||
viper.SetDefault("Sitemap", hugolib.Sitemap{Priority: -1})
|
viper.SetDefault("Sitemap", hugolib.Sitemap{Priority: -1})
|
||||||
viper.SetDefault("PygmentsStyle", "monokai")
|
viper.SetDefault("PygmentsStyle", "monokai")
|
||||||
viper.SetDefault("PygmentsUseClasses", false)
|
viper.SetDefault("PygmentsUseClasses", false)
|
||||||
|
viper.SetDefault("DisableLiveReload", false)
|
||||||
|
|
||||||
if hugoCmdV.PersistentFlags().Lookup("build-drafts").Changed {
|
if hugoCmdV.PersistentFlags().Lookup("buildDrafts").Changed {
|
||||||
viper.Set("BuildDrafts", Draft)
|
viper.Set("BuildDrafts", Draft)
|
||||||
}
|
}
|
||||||
|
|
||||||
if hugoCmdV.PersistentFlags().Lookup("uglyurls").Changed {
|
if hugoCmdV.PersistentFlags().Lookup("uglyUrls").Changed {
|
||||||
viper.Set("UglyUrls", UglyUrls)
|
viper.Set("UglyUrls", UglyUrls)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,7 +138,7 @@ func InitializeConfig() {
|
||||||
viper.Set("Verbose", Verbose)
|
viper.Set("Verbose", Verbose)
|
||||||
}
|
}
|
||||||
|
|
||||||
if hugoCmdV.PersistentFlags().Lookup("logfile").Changed {
|
if hugoCmdV.PersistentFlags().Lookup("logFile").Changed {
|
||||||
viper.Set("LogFile", LogFile)
|
viper.Set("LogFile", LogFile)
|
||||||
}
|
}
|
||||||
if BaseUrl != "" {
|
if BaseUrl != "" {
|
||||||
|
@ -323,16 +328,14 @@ func NewWatcher(port int) error {
|
||||||
fmt.Print("Static file changed, syncing\n\n")
|
fmt.Print("Static file changed, syncing\n\n")
|
||||||
utils.StopOnErr(copyStatic(), fmt.Sprintf("Error copying static files to %s", helpers.AbsPathify(viper.GetString("PublishDir"))))
|
utils.StopOnErr(copyStatic(), fmt.Sprintf("Error copying static files to %s", helpers.AbsPathify(viper.GetString("PublishDir"))))
|
||||||
|
|
||||||
// Tell livereload a js file changed to force a hard refresh
|
livereload.ForceRefresh()
|
||||||
wsHub.broadcast <- []byte(`{"command":"reload","path":"/x.js","originalPath":"","liveCSS":true}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if dynamic_changed {
|
if dynamic_changed {
|
||||||
fmt.Print("Change detected, rebuilding site\n\n")
|
fmt.Print("Change detected, rebuilding site\n\n")
|
||||||
utils.StopOnErr(buildSite(true))
|
utils.StopOnErr(buildSite(true))
|
||||||
|
|
||||||
// Tell livereload a js file changed to force a hard refresh
|
livereload.ForceRefresh()
|
||||||
wsHub.broadcast <- []byte(`{"command":"reload","path":"/x.js","originalPath":"","liveCSS":true}`)
|
|
||||||
}
|
}
|
||||||
case err := <-watcher.Error:
|
case err := <-watcher.Error:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -343,6 +346,12 @@ func NewWatcher(port int) error {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if port > 0 {
|
if port > 0 {
|
||||||
|
if !viper.GetBool("DisableLiveReload") {
|
||||||
|
livereload.Initialize()
|
||||||
|
http.HandleFunc("/livereload.js", livereload.ServeJS)
|
||||||
|
http.HandleFunc("/livereload", livereload.Handler)
|
||||||
|
}
|
||||||
|
|
||||||
go serve(port)
|
go serve(port)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -22,9 +21,6 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
//"code.google.com/p/go.net/websocket"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/hugo/helpers"
|
"github.com/spf13/hugo/helpers"
|
||||||
jww "github.com/spf13/jwalterweatherman"
|
jww "github.com/spf13/jwalterweatherman"
|
||||||
|
@ -34,12 +30,9 @@ import (
|
||||||
var serverPort int
|
var serverPort int
|
||||||
var serverWatch bool
|
var serverWatch bool
|
||||||
var serverAppend bool
|
var serverAppend bool
|
||||||
|
var disableLiveReload bool
|
||||||
|
|
||||||
func init() {
|
//var serverCmdV *cobra.Command
|
||||||
serverCmd.Flags().IntVarP(&serverPort, "port", "p", 1313, "port to run the server on")
|
|
||||||
serverCmd.Flags().BoolVarP(&serverWatch, "watch", "w", false, "watch filesystem for changes and recreate as needed")
|
|
||||||
serverCmd.Flags().BoolVarP(&serverAppend, "append-port", "", true, "append port to baseurl")
|
|
||||||
}
|
|
||||||
|
|
||||||
var serverCmd = &cobra.Command{
|
var serverCmd = &cobra.Command{
|
||||||
Use: "server",
|
Use: "server",
|
||||||
|
@ -47,7 +40,15 @@ var serverCmd = &cobra.Command{
|
||||||
Long: `Hugo is able to run it's own high performance web server.
|
Long: `Hugo is able to run it's own high performance web server.
|
||||||
Hugo will render all the files defined in the source directory and
|
Hugo will render all the files defined in the source directory and
|
||||||
Serve them up.`,
|
Serve them up.`,
|
||||||
Run: server,
|
//Run: server,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
serverCmd.Flags().IntVarP(&serverPort, "port", "p", 1313, "port to run the server on")
|
||||||
|
serverCmd.Flags().BoolVarP(&serverWatch, "watch", "w", false, "watch filesystem for changes and recreate as needed")
|
||||||
|
serverCmd.Flags().BoolVarP(&serverAppend, "appendPort", "", true, "append port to baseurl")
|
||||||
|
serverCmd.Flags().BoolVar(&disableLiveReload, "disableLiveReload", false, "watch without enabling live browser reload on rebuild")
|
||||||
|
serverCmd.Run = server
|
||||||
}
|
}
|
||||||
|
|
||||||
func server(cmd *cobra.Command, args []string) {
|
func server(cmd *cobra.Command, args []string) {
|
||||||
|
@ -57,6 +58,14 @@ func server(cmd *cobra.Command, args []string) {
|
||||||
BaseUrl = "http://localhost"
|
BaseUrl = "http://localhost"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cmd.Flags().Lookup("disableLiveReload").Changed {
|
||||||
|
viper.Set("DisableLiveReload", disableLiveReload)
|
||||||
|
}
|
||||||
|
|
||||||
|
if serverWatch {
|
||||||
|
viper.Set("Watch", true)
|
||||||
|
}
|
||||||
|
|
||||||
if !strings.HasPrefix(BaseUrl, "http://") {
|
if !strings.HasPrefix(BaseUrl, "http://") {
|
||||||
BaseUrl = "http://" + BaseUrl
|
BaseUrl = "http://" + BaseUrl
|
||||||
}
|
}
|
||||||
|
@ -96,111 +105,13 @@ func server(cmd *cobra.Command, args []string) {
|
||||||
|
|
||||||
func serve(port int) {
|
func serve(port int) {
|
||||||
jww.FEEDBACK.Println("Serving pages from " + helpers.AbsPathify(viper.GetString("PublishDir")))
|
jww.FEEDBACK.Println("Serving pages from " + helpers.AbsPathify(viper.GetString("PublishDir")))
|
||||||
|
|
||||||
jww.FEEDBACK.Printf("Web Server is available at %s\n", viper.GetString("BaseUrl"))
|
jww.FEEDBACK.Printf("Web Server is available at %s\n", viper.GetString("BaseUrl"))
|
||||||
|
|
||||||
fmt.Println("Press ctrl+c to stop")
|
fmt.Println("Press ctrl+c to stop")
|
||||||
|
|
||||||
http.Handle("/", http.FileServer(http.Dir(helpers.AbsPathify(viper.GetString("PublishDir")))))
|
http.Handle("/", http.FileServer(http.Dir(helpers.AbsPathify(viper.GetString("PublishDir")))))
|
||||||
go wsHub.run()
|
|
||||||
http.HandleFunc("/livereload", wsHandler)
|
|
||||||
|
|
||||||
err := http.ListenAndServe(":"+strconv.Itoa(port), nil)
|
err := http.ListenAndServe(":"+strconv.Itoa(port), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
jww.ERROR.Printf("Error: %s\n", err.Error())
|
jww.ERROR.Printf("Error: %s\n", err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type hub struct {
|
|
||||||
// Registered connections.
|
|
||||||
connections map[*connection]bool
|
|
||||||
|
|
||||||
// Inbound messages from the connections.
|
|
||||||
broadcast chan []byte
|
|
||||||
|
|
||||||
// Register requests from the connections.
|
|
||||||
register chan *connection
|
|
||||||
|
|
||||||
// Unregister requests from connections.
|
|
||||||
unregister chan *connection
|
|
||||||
}
|
|
||||||
|
|
||||||
var wsHub = hub{
|
|
||||||
broadcast: make(chan []byte),
|
|
||||||
register: make(chan *connection),
|
|
||||||
unregister: make(chan *connection),
|
|
||||||
connections: make(map[*connection]bool),
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *hub) run() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case c := <-h.register:
|
|
||||||
h.connections[c] = true
|
|
||||||
case c := <-h.unregister:
|
|
||||||
delete(h.connections, c)
|
|
||||||
close(c.send)
|
|
||||||
case m := <-h.broadcast:
|
|
||||||
for c := range h.connections {
|
|
||||||
select {
|
|
||||||
case c.send <- m:
|
|
||||||
default:
|
|
||||||
delete(h.connections, c)
|
|
||||||
close(c.send)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type connection struct {
|
|
||||||
// The websocket connection.
|
|
||||||
ws *websocket.Conn
|
|
||||||
|
|
||||||
// Buffered channel of outbound messages.
|
|
||||||
send chan []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *connection) reader() {
|
|
||||||
for {
|
|
||||||
_, message, err := c.ws.ReadMessage()
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
fmt.Println(string(message))
|
|
||||||
switch true {
|
|
||||||
case bytes.Contains(message, []byte(`"command":"hello"`)):
|
|
||||||
wsHub.broadcast <- []byte(`{
|
|
||||||
"command": "hello",
|
|
||||||
"protocols": [ "http://livereload.com/protocols/official-7" ],
|
|
||||||
"serverName": "Hugo"
|
|
||||||
}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.ws.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *connection) writer() {
|
|
||||||
for message := range c.send {
|
|
||||||
err := c.ws.WriteMessage(websocket.TextMessage, message)
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.ws.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
var upgrader = &websocket.Upgrader{ReadBufferSize: 1024, WriteBufferSize: 1024}
|
|
||||||
|
|
||||||
func wsHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ws, err := upgrader.Upgrade(w, r, nil)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c := &connection{send: make(chan []byte, 256), ws: ws}
|
|
||||||
wsHub.register <- c
|
|
||||||
defer func() { wsHub.unregister <- c }()
|
|
||||||
go c.writer()
|
|
||||||
c.reader()
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,6 +3,5 @@
|
||||||
<link href="/static/css/hugofont.css" rel="stylesheet"/>
|
<link href="/static/css/hugofont.css" rel="stylesheet"/>
|
||||||
<link href='http://fonts.googleapis.com/css?family=Arbutus+Slab&family=Cabin:600&family=Source+Code+Pro' rel='stylesheet' type='text/css'>
|
<link href='http://fonts.googleapis.com/css?family=Arbutus+Slab&family=Cabin:600&family=Source+Code+Pro' rel='stylesheet' type='text/css'>
|
||||||
<link rel="stylesheet" href="/static/css/monokai_sublime.css">
|
<link rel="stylesheet" href="/static/css/monokai_sublime.css">
|
||||||
<script src="/static/js/livereload.js?host=localhost&port=1313"></script>
|
|
||||||
<script src="/static/js/highlight.pack.js"></script>
|
<script src="/static/js/highlight.pack.js"></script>
|
||||||
<script>hljs.initHighlightingOnLoad();</script>
|
<script>hljs.initHighlightingOnLoad();</script>
|
||||||
|
|
|
@ -864,6 +864,10 @@ func (s *Site) render(d interface{}, out string, layouts ...string) (err error)
|
||||||
transformLinks = append(transformLinks, absURL...)
|
transformLinks = append(transformLinks, absURL...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if viper.GetBool("watch") && !viper.GetBool("DisableLiveReload") {
|
||||||
|
transformLinks = append(transformLinks, transform.LiveReloadInject)
|
||||||
|
}
|
||||||
|
|
||||||
transformer := transform.NewChain(transformLinks...)
|
transformer := transform.NewChain(transformLinks...)
|
||||||
|
|
||||||
var renderBuffer *bytes.Buffer
|
var renderBuffer *bytes.Buffer
|
||||||
|
|
56
livereload/connection.go
Normal file
56
livereload/connection.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright © 2014 Steve Francia <spf@spf13.com>.
|
||||||
|
//
|
||||||
|
// Licensed under the Simple Public License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
// http://opensource.org/licenses/Simple-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package livereload
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
type connection struct {
|
||||||
|
// The websocket connection.
|
||||||
|
ws *websocket.Conn
|
||||||
|
|
||||||
|
// Buffered channel of outbound messages.
|
||||||
|
send chan []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *connection) reader() {
|
||||||
|
for {
|
||||||
|
_, message, err := c.ws.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
switch true {
|
||||||
|
case bytes.Contains(message, []byte(`"command":"hello"`)):
|
||||||
|
wsHub.broadcast <- []byte(`{
|
||||||
|
"command": "hello",
|
||||||
|
"protocols": [ "http://livereload.com/protocols/official-7" ],
|
||||||
|
"serverName": "Hugo"
|
||||||
|
}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.ws.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *connection) writer() {
|
||||||
|
for message := range c.send {
|
||||||
|
err := c.ws.WriteMessage(websocket.TextMessage, message)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.ws.Close()
|
||||||
|
}
|
56
livereload/hub.go
Normal file
56
livereload/hub.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright © 2014 Steve Francia <spf@spf13.com>.
|
||||||
|
//
|
||||||
|
// Licensed under the Simple Public License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
// http://opensource.org/licenses/Simple-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package livereload
|
||||||
|
|
||||||
|
type hub struct {
|
||||||
|
// Registered connections.
|
||||||
|
connections map[*connection]bool
|
||||||
|
|
||||||
|
// Inbound messages from the connections.
|
||||||
|
broadcast chan []byte
|
||||||
|
|
||||||
|
// Register requests from the connections.
|
||||||
|
register chan *connection
|
||||||
|
|
||||||
|
// Unregister requests from connections.
|
||||||
|
unregister chan *connection
|
||||||
|
}
|
||||||
|
|
||||||
|
var wsHub = hub{
|
||||||
|
broadcast: make(chan []byte),
|
||||||
|
register: make(chan *connection),
|
||||||
|
unregister: make(chan *connection),
|
||||||
|
connections: make(map[*connection]bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *hub) run() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case c := <-h.register:
|
||||||
|
h.connections[c] = true
|
||||||
|
case c := <-h.unregister:
|
||||||
|
delete(h.connections, c)
|
||||||
|
close(c.send)
|
||||||
|
case m := <-h.broadcast:
|
||||||
|
for c := range h.connections {
|
||||||
|
select {
|
||||||
|
case c.send <- m:
|
||||||
|
default:
|
||||||
|
delete(h.connections, c)
|
||||||
|
close(c.send)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
livereload/livereload.go
Normal file
49
livereload/livereload.go
Normal file
File diff suppressed because one or more lines are too long
19
transform/livereloadinject.go
Normal file
19
transform/livereloadinject.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package transform
|
||||||
|
|
||||||
|
import "bytes"
|
||||||
|
|
||||||
|
func LiveReloadInject(content []byte) []byte {
|
||||||
|
match := []byte("</body>")
|
||||||
|
replace := []byte(`<script>document.write('<script src="http://'
|
||||||
|
+ (location.host || 'localhost').split(':')[0]
|
||||||
|
+ ':1313/livereload.js?mindelay=10"></'
|
||||||
|
+ 'script>')</script></body>`)
|
||||||
|
newcontent := bytes.Replace(content, match, replace, -1)
|
||||||
|
|
||||||
|
if len(newcontent) == len(content) {
|
||||||
|
match := []byte("</BODY>")
|
||||||
|
newcontent = bytes.Replace(content, match, replace, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newcontent
|
||||||
|
}
|
Loading…
Reference in a new issue