Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom "pathFromUrl" to remove digest/hash from asset filename #75

Open
steffenweber opened this issue Jan 22, 2018 · 2 comments
Open

Comments

@steffenweber
Copy link

First of all, thank you for implementing issue #17!

My asset hrefs contain a digest/hash, i.e. the main.css on disk is referenced as main.eec09356.css in the HTML (which is then rewritten to main.css by the webserver). The "changed" URL sent to livereload-js is just main.css. Therefore nothing matches with reloadMissingCSS = false.

I think I'd have to modify the pathFromUrl function to support this setup:

path = path.replace(/\.[0-9a-f]{8}\./, '.');

What would be the best way to make this feature optional / configurable such that it can be merged? Maybe a new pathFromUrl option that (if present) would be used instead of the default pathFromUrl function?

window.LiveReloadOptions = {
	pathFromUrl: function(url) {
		return url.replace(/\.[0-9a-f]{8}\./, '.');
	}
};
@smhg
Copy link
Contributor

smhg commented Jan 24, 2018

Sounds reasonable to me. There is no way the server can be aware of how to transform this, right?

@steffenweber
Copy link
Author

steffenweber commented Jan 24, 2018

The server (gulp + tiny-lr in my case) could compute the new digest/hash but it does not know the old digest/hash. However, it could provide the client with a regex to transform the path if you'd prefer making this configurable in the server instead of in window.LiveReloadOptions.

The following patch adds a client-side option pathRewriteFn that is handled inside pathFromUrl. I had to move this function into the Reloader class such that it has access to the @options object. I've furthermore renamed the old @options object to @reloadOptions (such that @options refers to the same object as in other source files).

diff --git a/src/livereload.coffee b/src/livereload.coffee
index 06f2579..0073400 100644
--- a/src/livereload.coffee
+++ b/src/livereload.coffee
@@ -40,7 +40,7 @@ exports.LiveReload = class LiveReload
         return
 
     # i can haz reloader?
-    @reloader = new Reloader(@window, @console, Timer)
+    @reloader = new Reloader(@options, @window, @console, Timer)
 
     # i can haz connection?
     @connector = new Connector @options, @WebSocket, Timer,
diff --git a/src/options.coffee b/src/options.coffee
index 057ba30..2cb7393 100644
--- a/src/options.coffee
+++ b/src/options.coffee
@@ -12,6 +12,8 @@ exports.Options = class Options
     @maxdelay = 60000
     @handshake_timeout = 5000
 
+    @pathRewriteFn = null
+
   set: (name, value) ->
     if typeof value is 'undefined'
       return
diff --git a/src/reloader.coffee b/src/reloader.coffee
index 4df0a52..56de2b0 100644
--- a/src/reloader.coffee
+++ b/src/reloader.coffee
@@ -20,17 +20,6 @@ splitUrl = (url) ->
 
   return { url, params, hash }
 
-pathFromUrl = (url) ->
-  url = splitUrl(url).url
-  if url.indexOf('file://') == 0
-    path = url.replace ///^ file:// (localhost)? ///, ''
-  else
-    #                        http  :   // hostname  :8080  /
-    path = url.replace ///^ ([^:]+ :)? // ([^:/]+) (:\d*)? / ///, '/'
-
-  # decodeURI has special handling of stuff like semicolons, so use decodeURIComponent
-  return decodeURIComponent(path)
-
 pickBestMatch = (path, objects, pathFunc) ->
   bestMatch = { score: 0 }
   for object in objects
@@ -68,7 +57,7 @@ IMAGE_STYLES = [
 
 exports.Reloader = class Reloader
 
-  constructor: (@window, @console, @Timer) ->
+  constructor: (@options, @window, @console, @Timer) ->
     @document = @window.document
     @importCacheWaitPeriod = 200
     @plugins = []
@@ -83,8 +72,8 @@ exports.Reloader = class Reloader
 
 
   reload: (path, options) ->
-    @options = options  # avoid passing it through all the funcs
-    @options.stylesheetReloadTimeout ?= 15000
+    @reloadOptions = options  # avoid passing it through all the funcs
+    @reloadOptions.stylesheetReloadTimeout ?= 15000
     for plugin in @plugins
       if plugin.reload && plugin.reload(path, options)
         return
@@ -109,7 +98,7 @@ exports.Reloader = class Reloader
     expando = @generateUniqueString()
 
     for img in this.document.images
-      if pathsMatch(path, pathFromUrl(img.src))
+      if pathsMatch(path, @pathFromUrl(img.src))
         img.src = @generateCacheBustUrl(img.src, expando)
 
     if @document.querySelectorAll
@@ -147,7 +136,7 @@ exports.Reloader = class Reloader
       value = style[styleName]
       if typeof value is 'string'
         newValue = value.replace ///\b url \s* \( ([^)]*) \) ///, (match, src) =>
-          if pathsMatch(path, pathFromUrl(src))
+          if pathsMatch(path, @pathFromUrl(src))
             "url(#{@generateCacheBustUrl(src, expando)})"
           else
             match
@@ -173,7 +162,7 @@ exports.Reloader = class Reloader
         links.push style
 
     @console.log "LiveReload found #{links.length} LINKed stylesheets, #{imported.length} @imported stylesheets"
-    match = pickBestMatch(path, links.concat(imported), (l) => pathFromUrl(@linkHref(l)))
+    match = pickBestMatch(path, links.concat(imported), (l) => @pathFromUrl(@linkHref(l)))
 
     if match
       if match.object.rule
@@ -241,7 +230,7 @@ exports.Reloader = class Reloader
           @Timer.start 50, poll
 
     # fail safe
-    @Timer.start @options.stylesheetReloadTimeout, executeCallback
+    @Timer.start @reloadOptions.stylesheetReloadTimeout, executeCallback
 
 
   linkHref: (link) ->
@@ -335,10 +324,10 @@ exports.Reloader = class Reloader
   generateCacheBustUrl: (url, expando=@generateUniqueString()) ->
     { url, hash, params: oldParams } = splitUrl(url)
 
-    if @options.overrideURL
-      if url.indexOf(@options.serverURL) < 0
+    if @reloadOptions.overrideURL
+      if url.indexOf(@reloadOptions.serverURL) < 0
         originalUrl = url
-        url = @options.serverURL + @options.overrideURL + "?url=" + encodeURIComponent(url)
+        url = @reloadOptions.serverURL + @reloadOptions.overrideURL + "?url=" + encodeURIComponent(url)
         @console.log "LiveReload is overriding source URL #{originalUrl} with #{url}"
 
     params = oldParams.replace /(\?|&)livereload=(\d+)/, (match, sep) -> "#{sep}#{expando}"
@@ -349,3 +338,19 @@ exports.Reloader = class Reloader
         params = "#{oldParams}&#{expando}"
 
     return url + params + hash
+
+  pathFromUrl: (url) ->
+    url = splitUrl(url).url
+    if url.indexOf('file://') == 0
+      path = url.replace ///^ file:// (localhost)? ///, ''
+    else
+      #                        http  :   // hostname  :8080  /
+      path = url.replace ///^ ([^:]+ :)? // ([^:/]+) (:\d*)? / ///, '/'
+
+    # decodeURI has special handling of stuff like semicolons, so use decodeURIComponent
+    path = decodeURIComponent(path)
+
+    if typeof @options.pathRewriteFn == 'function'
+      path = @options.pathRewriteFn(path)
+
+    return path

Example usage:

window.LiveReloadOptions = {
    host: 'localhost',
    pathRewriteFn: function(path) {
        return path.replace(/\.[0-9a-f]{8}\./, '.');
    }
};

What do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants