shithub: hugo

Download patch

ref: df489b4712dd59b097de0d2fb84cfac7a013f5cd
parent: 0099b5a3cb922ac3f844b5d9d9f098cd1003b0f5
author: bep <[email protected]>
date: Wed Sep 10 08:21:22 EDT 2014

Enable soft livereload of CSS and images

Prior to this commit a dummy JavaScript filename was sent to LiveReload when changing a static file (CSS, image etc.), forcing a full browser reload of the page.

This commit fixes this by sending the relative file path of the changed static resource, enabling partial live reloading for CSS- and image-changes. If more than one static file happens to end up in the same changeevent-batch, it will fall back to do a full refresh. To enable this logic, the change events with names ending with ".goutputstream*" is now filtered out as temporary.

Changes in dynamic content behaves like before.

Issue #490

--- a/commands/hugo.go
+++ b/commands/hugo.go
@@ -312,10 +312,11 @@
 
 				static_changed := false
 				dynamic_changed := false
+				static_files_changed := make(map[string]bool)
 
 				for _, ev := range evs {
 					ext := filepath.Ext(ev.Name)
-					istemp := strings.HasSuffix(ext, "~") || (ext == ".swp") || (ext == ".tmp")
+					istemp := strings.HasSuffix(ext, "~") || (ext == ".swp") || (ext == ".tmp") || (strings.HasPrefix(ext, ".goutputstream"))
 					if istemp {
 						continue
 					}
@@ -328,6 +329,12 @@
 					static_changed = static_changed || isstatic
 					dynamic_changed = dynamic_changed || !isstatic
 
+					if isstatic {
+						if staticPath, err := helpers.MakeStaticPathRelative(ev.Name); err == nil {
+							static_files_changed[staticPath] = true
+						}
+					}
+
 					// add new directory to watch list
 					if s, err := os.Stat(ev.Name); err == nil && s.Mode().IsDir() {
 						if ev.IsCreate() {
@@ -342,7 +349,16 @@
 
 					if !viper.GetBool("DisableLiveReload") {
 						// Will block forever trying to write to a channel that nobody is reading if livereload isn't initalized
-						livereload.ForceRefresh()
+
+						// force refresh when more than one file
+						if len(static_files_changed) == 1 {
+							for path := range static_files_changed {
+								livereload.RefreshPath(path)
+							}
+
+						} else {
+							livereload.ForceRefresh()
+						}
 					}
 				}
 
--- a/helpers/helpers_test.go
+++ b/helpers/helpers_test.go
@@ -122,3 +122,26 @@
 		}
 	}
 }
+
+func TestMakePathRelative(t *testing.T) {
+	type test struct {
+		inPath, path1, path2, output string
+	}
+
+	data := []test{
+		{"/abc/bcd/ab.css", "/abc/bcd", "/bbc/bcd", "/ab.css"},
+		{"/abc/bcd/ab.css", "/abcd/bcd", "/abc/bcd", "/ab.css"},
+	}
+
+	for i, d := range data {
+		output, _ := MakePathRelative(d.inPath, d.path1, d.path2)
+		if d.output != output {
+			t.Errorf("Test #%d failed. Expected %q got %q", i, d.output, output)
+		}
+	}
+	_, error := MakePathRelative("a/b/c.ss", "/a/c", "/d/c", "/e/f")
+
+	if error == nil {
+		t.Errorf("Test #%d failed. Expected error")
+	}
+}
--- a/helpers/path.go
+++ b/helpers/path.go
@@ -14,6 +14,7 @@
 package helpers
 
 import (
+	"errors"
 	"fmt"
 	"io"
 	"os"
@@ -128,6 +129,23 @@
 	}
 
 	return filepath.Clean(filepath.Join(viper.GetString("WorkingDir"), inPath))
+}
+
+func MakeStaticPathRelative(inPath string) (string, error) {
+	staticDir := AbsPathify(viper.GetString("StaticDir"))
+	themeStaticDir := AbsPathify("themes/"+viper.GetString("theme")) + "/static/"
+
+	return MakePathRelative(inPath, staticDir, themeStaticDir)
+}
+
+func MakePathRelative(inPath string, possibleDirectories ...string) (string, error) {
+
+	for _, currentPath := range possibleDirectories {
+		if strings.HasPrefix(inPath, currentPath) {
+			return strings.TrimPrefix(inPath, currentPath), nil
+		}
+	}
+	return inPath, errors.New("Can't extract relative path, unknown prefix")
 }
 
 func Filename(in string) (name string) {
--- a/livereload/livereload.go
+++ b/livereload/livereload.go
@@ -39,7 +39,12 @@
 
 func ForceRefresh() {
 	// Tell livereload a js file changed to force a hard refresh
-	wsHub.broadcast <- []byte(`{"command":"reload","path":"/x.js","originalPath":"","liveCSS":true}`)
+	RefreshPath("/x.js")
+}
+
+func RefreshPath(s string) {
+	// Tell livereload a file has changed - will force a hard refresh if not CSS or an image
+	wsHub.broadcast <- []byte(`{"command":"reload","path":"` + s + "\"" + `,"originalPath":"","liveCSS":true,"liveImg":true}`)
 }
 
 func ServeJS(w http.ResponseWriter, r *http.Request) {