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

refresh css in browser (dev mode) without having to reload the page #308

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions lib/volt/page/tasks.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'volt/utils/ejson'
require 'securerandom'

module Volt
# The tasks class provides an interface to call tasks on
Expand Down Expand Up @@ -36,6 +37,8 @@ def received_message(name, promise_id, *args)
response(promise_id, *args)
when 'reload'
reload
when 'refresh_css'
refresh_css(*args)
end
end

Expand Down Expand Up @@ -82,5 +85,40 @@ def reload
Volt.current_app.page._reloading = true
`window.location.reload(false);`
end

# refresh changed css
def refresh_css(changed_files)
changed_files[:removed].each do |path|

# Remove link to css from head
`
var el = window.document.querySelector("link[href^='" + path + "']");
el.parentElement.removeChild(el);
`
end
changed_files[:modified].each do |path|

# We fetch the link
# We then invalidate the cached css by appending a random query to the href which forces the CSS to be reloaded
`
var el = window.document.querySelector("link[href^='" + path + "']")
el.setAttribute('href', el.getAttribute('href') + '?v=' + #{SecureRandom.uuid[0..7]})
`
end

changed_files[:added].each do |path|

# Inject a new link to the css into the head
`
link=document.createElement('link');
link.href=path;
link.rel='stylesheet';
link.type='text/css';
link.media='all';

document.getElementsByTagName('head')[0].appendChild(link);
`
end
end
end
end
68 changes: 44 additions & 24 deletions lib/volt/server/forking_server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -208,38 +208,58 @@ def call(env)
end
end

def reload(args)
changed_files = args[:modified] + args[:added] + args[:removed]

def reload(changed_files)
# only reload the server code if a non-view file was changed
server_code_changed = changed_files.any? { |path| File.extname(path) == '.rb' }
# if all changed files are just css files then we will just refresh the css links by invalidating the cache
if changed_files.all? { |path| File.extname(path).match(/css|scss|sass/) }
msg = 'CSS updated, refreshing client...'

msg = 'file changed, reloading'
msg << ' server and' if server_code_changed
msg << ' client...'
Volt.logger.log_with_color(msg, :light_blue)

Volt.logger.log_with_color(msg, :light_blue)
# sub out the Volt root and any preprocessor extensions from the file paths
css_file_paths = args.update(args) do |key,value|
value.map { |path| path.gsub(Volt.root, '').gsub(/.scss|.sass/,'') }
end

begin
SocketConnectionHandler.send_message_all(nil, 'refresh_css', nil, css_file_paths)
rescue => e
Volt.logger.error('CSS refresh dispatch error: ')
Volt.logger.error(e)
end
else
# only reload the server code if a non-view file was changed
server_code_changed = changed_files.any? { |path| File.extname(path) == '.rb' }

# Figure out if any views or routes were changed:
# TODO: Might want to only check for /config/ under the CWD
if changed_files.any? {|path| path =~ /\/config\// }
update_mod_time
sync_mod_time
end
msg = 'file changed, reloading'
msg << ' server and' if server_code_changed
msg << ' client...'

begin
SocketConnectionHandler.send_message_all(nil, 'reload')
rescue => e
Volt.logger.error('Reload dispatch error: ')
Volt.logger.error(e)
end
Volt.logger.log_with_color(msg, :light_blue)

if server_code_changed
@child_lock.with_write_lock do
stop_child
start_child

# Figure out if any views or routes were changed:
# TODO: Might want to only check for /config/ under the CWD
if changed_files.any? {|path| path =~ /\/config\// }
update_mod_time
sync_mod_time
end

begin
SocketConnectionHandler.send_message_all(nil, 'reload')
rescue => e
Volt.logger.error('Reload dispatch error: ')
Volt.logger.error(e)
end

if server_code_changed
@child_lock.with_write_lock do
stop_child
start_child
sync_mod_time
end
end
end
end

Expand Down Expand Up @@ -267,7 +287,7 @@ def start_change_listener
@listener = Listen.to("#{@server.app_path}/", options) do |modified, added, removed|
Thread.new do
# Run the reload in a new thread
reload(modified + added + removed)
reload(modified: modified, added: added, removed: removed)
end
end
@listener.start
Expand Down
1 change: 1 addition & 0 deletions lib/volt/server/socket_connection_handler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def self.channels
end

# Sends a message to all, optionally skipping a users channel
# Expects promise_id after message name, can be nil
def self.send_message_all(skip_channel = nil, *args)
return unless defined?(@@channels)
@@channels.each do |channel|
Expand Down