hotwire-livereload 1.3.2 → 1.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +32 -1
- data/app/assets/javascripts/hotwire-livereload.js +57 -50
- data/app/javascript/hotwire-livereload.js +11 -1
- data/lib/hotwire/livereload/engine.rb +35 -5
- data/lib/hotwire/livereload/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 04b3a325fa089d62215f26cf3fbac951e3ac91d0b03af7aed02fa2a49075969e
|
4
|
+
data.tar.gz: 24e7339a71da29f35b1930f95f18f0c035aa0221b411fe4a58f27d10384dbb76
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 01e7cc071040c5fbafa3fac11adcdfff171695a82c6d6d90db06c8d2010032beb4172ae667b029512737c0afb79c005bcd2e54084661ff662bbae28157087a68
|
7
|
+
data.tar.gz: 41e86aa960b2796d65aa26e1d8034a819e5ecdfea578b5995e275f713d666f9b4ffd6a9c6337a6bf17834394ac194382aa2ddf9ff74843fb0f7644ed20bfd2fa
|
data/README.md
CHANGED
@@ -20,7 +20,7 @@ Run installer:
|
|
20
20
|
rails livereload:install
|
21
21
|
```
|
22
22
|
|
23
|
-
Folders
|
23
|
+
Folders watched by default:
|
24
24
|
- `app/views`
|
25
25
|
- `app/helpers`
|
26
26
|
- `app/javascript`
|
@@ -32,6 +32,12 @@ Folders listened by default:
|
|
32
32
|
|
33
33
|
The gem detects if you use [`jsbundling-rails`](https://github.com/rails/jsbundling-rails) or [`cssbundling-rails`](https://github.com/rails/cssbundling-rails) and watches for changes in their output folder `app/assets/builds` automatically.
|
34
34
|
|
35
|
+
In your layout, make sure you don't `turbo-track` your JS/CSS in development:
|
36
|
+
```diff
|
37
|
+
+ <%= stylesheet_link_tag "application", "data-turbo-track": Rails.env.production? ? "reload" : "" %>
|
38
|
+
- <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
|
39
|
+
```
|
40
|
+
|
35
41
|
## Configuration
|
36
42
|
|
37
43
|
### Listen paths
|
@@ -46,6 +52,16 @@ Rails.application.configure do
|
|
46
52
|
end
|
47
53
|
```
|
48
54
|
|
55
|
+
You can skip one or few default listen paths:
|
56
|
+
```ruby
|
57
|
+
# config/environments/development.rb
|
58
|
+
|
59
|
+
Rails.application.configure do
|
60
|
+
# ...
|
61
|
+
config.hotwire_livereload.skip_listen_paths << Rails.root.join("app/views")
|
62
|
+
end
|
63
|
+
```
|
64
|
+
|
49
65
|
You can disable default listen paths and fully override them:
|
50
66
|
```ruby
|
51
67
|
# config/environments/development.rb
|
@@ -112,6 +128,21 @@ Rails.application.configure do
|
|
112
128
|
end
|
113
129
|
```
|
114
130
|
|
131
|
+
### Listen debounce delay
|
132
|
+
|
133
|
+
If your app uses TailwindCSS or similar that compiles your CSS from looking at your templates, you can end up in a situation, where updating a template triggers twice for changes; once for the template and once for the rebuilt CSS. This can lead to unreliable reloads, ie. the reload happening before the CSS is built.
|
134
|
+
|
135
|
+
To avoid this, you can add a debounce delay to the file watcher:
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
# config/environments/development.rb
|
139
|
+
|
140
|
+
Rails.application.configure do
|
141
|
+
# ...
|
142
|
+
config.hotwire_livereload.debounce_delay_ms = 300 # in milliseconds
|
143
|
+
end
|
144
|
+
```
|
145
|
+
|
115
146
|
## Disable livereload
|
116
147
|
|
117
148
|
To temporarily disable livereload use:
|
@@ -396,19 +396,19 @@
|
|
396
396
|
this.subscriptions = subscriptions;
|
397
397
|
this.pendingSubscriptions = [];
|
398
398
|
}
|
399
|
-
SubscriptionGuarantor2.prototype.guarantee = function guarantee(
|
400
|
-
if (this.pendingSubscriptions.indexOf(
|
401
|
-
logger.log("SubscriptionGuarantor guaranteeing " +
|
402
|
-
this.pendingSubscriptions.push(
|
399
|
+
SubscriptionGuarantor2.prototype.guarantee = function guarantee(subscription2) {
|
400
|
+
if (this.pendingSubscriptions.indexOf(subscription2) == -1) {
|
401
|
+
logger.log("SubscriptionGuarantor guaranteeing " + subscription2.identifier);
|
402
|
+
this.pendingSubscriptions.push(subscription2);
|
403
403
|
} else {
|
404
|
-
logger.log("SubscriptionGuarantor already guaranteeing " +
|
404
|
+
logger.log("SubscriptionGuarantor already guaranteeing " + subscription2.identifier);
|
405
405
|
}
|
406
406
|
this.startGuaranteeing();
|
407
407
|
};
|
408
|
-
SubscriptionGuarantor2.prototype.forget = function forget(
|
409
|
-
logger.log("SubscriptionGuarantor forgetting " +
|
408
|
+
SubscriptionGuarantor2.prototype.forget = function forget(subscription2) {
|
409
|
+
logger.log("SubscriptionGuarantor forgetting " + subscription2.identifier);
|
410
410
|
this.pendingSubscriptions = this.pendingSubscriptions.filter(function(s) {
|
411
|
-
return s !==
|
411
|
+
return s !== subscription2;
|
412
412
|
});
|
413
413
|
};
|
414
414
|
SubscriptionGuarantor2.prototype.startGuaranteeing = function startGuaranteeing() {
|
@@ -422,9 +422,9 @@
|
|
422
422
|
var _this = this;
|
423
423
|
this.retryTimeout = setTimeout(function() {
|
424
424
|
if (_this.subscriptions && typeof _this.subscriptions.subscribe === "function") {
|
425
|
-
_this.pendingSubscriptions.map(function(
|
426
|
-
logger.log("SubscriptionGuarantor resubscribing " +
|
427
|
-
_this.subscriptions.subscribe(
|
425
|
+
_this.pendingSubscriptions.map(function(subscription2) {
|
426
|
+
logger.log("SubscriptionGuarantor resubscribing " + subscription2.identifier);
|
427
|
+
_this.subscriptions.subscribe(subscription2);
|
428
428
|
});
|
429
429
|
}
|
430
430
|
}, 500);
|
@@ -443,37 +443,37 @@
|
|
443
443
|
var params = (typeof channel === "undefined" ? "undefined" : _typeof(channel)) === "object" ? channel : {
|
444
444
|
channel
|
445
445
|
};
|
446
|
-
var
|
447
|
-
return this.add(
|
446
|
+
var subscription2 = new Subscription(this.consumer, params, mixin);
|
447
|
+
return this.add(subscription2);
|
448
448
|
};
|
449
|
-
Subscriptions2.prototype.add = function add(
|
450
|
-
this.subscriptions.push(
|
449
|
+
Subscriptions2.prototype.add = function add(subscription2) {
|
450
|
+
this.subscriptions.push(subscription2);
|
451
451
|
this.consumer.ensureActiveConnection();
|
452
|
-
this.notify(
|
453
|
-
this.subscribe(
|
454
|
-
return
|
455
|
-
};
|
456
|
-
Subscriptions2.prototype.remove = function remove2(
|
457
|
-
this.forget(
|
458
|
-
if (!this.findAll(
|
459
|
-
this.sendCommand(
|
452
|
+
this.notify(subscription2, "initialized");
|
453
|
+
this.subscribe(subscription2);
|
454
|
+
return subscription2;
|
455
|
+
};
|
456
|
+
Subscriptions2.prototype.remove = function remove2(subscription2) {
|
457
|
+
this.forget(subscription2);
|
458
|
+
if (!this.findAll(subscription2.identifier).length) {
|
459
|
+
this.sendCommand(subscription2, "unsubscribe");
|
460
460
|
}
|
461
|
-
return
|
461
|
+
return subscription2;
|
462
462
|
};
|
463
463
|
Subscriptions2.prototype.reject = function reject(identifier) {
|
464
464
|
var _this = this;
|
465
|
-
return this.findAll(identifier).map(function(
|
466
|
-
_this.forget(
|
467
|
-
_this.notify(
|
468
|
-
return
|
465
|
+
return this.findAll(identifier).map(function(subscription2) {
|
466
|
+
_this.forget(subscription2);
|
467
|
+
_this.notify(subscription2, "rejected");
|
468
|
+
return subscription2;
|
469
469
|
});
|
470
470
|
};
|
471
|
-
Subscriptions2.prototype.forget = function forget(
|
472
|
-
this.guarantor.forget(
|
471
|
+
Subscriptions2.prototype.forget = function forget(subscription2) {
|
472
|
+
this.guarantor.forget(subscription2);
|
473
473
|
this.subscriptions = this.subscriptions.filter(function(s) {
|
474
|
-
return s !==
|
474
|
+
return s !== subscription2;
|
475
475
|
});
|
476
|
-
return
|
476
|
+
return subscription2;
|
477
477
|
};
|
478
478
|
Subscriptions2.prototype.findAll = function findAll(identifier) {
|
479
479
|
return this.subscriptions.filter(function(s) {
|
@@ -482,8 +482,8 @@
|
|
482
482
|
};
|
483
483
|
Subscriptions2.prototype.reload = function reload() {
|
484
484
|
var _this2 = this;
|
485
|
-
return this.subscriptions.map(function(
|
486
|
-
return _this2.subscribe(
|
485
|
+
return this.subscriptions.map(function(subscription2) {
|
486
|
+
return _this2.subscribe(subscription2);
|
487
487
|
});
|
488
488
|
};
|
489
489
|
Subscriptions2.prototype.notifyAll = function notifyAll(callbackName) {
|
@@ -491,38 +491,38 @@
|
|
491
491
|
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
492
492
|
args[_key - 1] = arguments[_key];
|
493
493
|
}
|
494
|
-
return this.subscriptions.map(function(
|
495
|
-
return _this3.notify.apply(_this3, [
|
494
|
+
return this.subscriptions.map(function(subscription2) {
|
495
|
+
return _this3.notify.apply(_this3, [subscription2, callbackName].concat(args));
|
496
496
|
});
|
497
497
|
};
|
498
|
-
Subscriptions2.prototype.notify = function notify(
|
498
|
+
Subscriptions2.prototype.notify = function notify(subscription2, callbackName) {
|
499
499
|
for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
|
500
500
|
args[_key2 - 2] = arguments[_key2];
|
501
501
|
}
|
502
502
|
var subscriptions = void 0;
|
503
|
-
if (typeof
|
504
|
-
subscriptions = this.findAll(
|
503
|
+
if (typeof subscription2 === "string") {
|
504
|
+
subscriptions = this.findAll(subscription2);
|
505
505
|
} else {
|
506
|
-
subscriptions = [
|
506
|
+
subscriptions = [subscription2];
|
507
507
|
}
|
508
|
-
return subscriptions.map(function(
|
509
|
-
return typeof
|
508
|
+
return subscriptions.map(function(subscription3) {
|
509
|
+
return typeof subscription3[callbackName] === "function" ? subscription3[callbackName].apply(subscription3, args) : void 0;
|
510
510
|
});
|
511
511
|
};
|
512
|
-
Subscriptions2.prototype.subscribe = function subscribe(
|
513
|
-
if (this.sendCommand(
|
514
|
-
this.guarantor.guarantee(
|
512
|
+
Subscriptions2.prototype.subscribe = function subscribe(subscription2) {
|
513
|
+
if (this.sendCommand(subscription2, "subscribe")) {
|
514
|
+
this.guarantor.guarantee(subscription2);
|
515
515
|
}
|
516
516
|
};
|
517
517
|
Subscriptions2.prototype.confirmSubscription = function confirmSubscription(identifier) {
|
518
518
|
var _this4 = this;
|
519
519
|
logger.log("Subscription confirmed " + identifier);
|
520
|
-
this.findAll(identifier).map(function(
|
521
|
-
return _this4.guarantor.forget(
|
520
|
+
this.findAll(identifier).map(function(subscription2) {
|
521
|
+
return _this4.guarantor.forget(subscription2);
|
522
522
|
});
|
523
523
|
};
|
524
|
-
Subscriptions2.prototype.sendCommand = function sendCommand(
|
525
|
-
var identifier =
|
524
|
+
Subscriptions2.prototype.sendCommand = function sendCommand(subscription2, command) {
|
525
|
+
var identifier = subscription2.identifier;
|
526
526
|
return this.consumer.send({
|
527
527
|
command,
|
528
528
|
identifier
|
@@ -704,7 +704,8 @@
|
|
704
704
|
|
705
705
|
// app/javascript/hotwire-livereload.js
|
706
706
|
var consumer = (0, import_actioncable.createConsumer)();
|
707
|
-
|
707
|
+
var subscription = null;
|
708
|
+
var createSubscription = () => consumer.subscriptions.create("Hotwire::Livereload::ReloadChannel", {
|
708
709
|
received: hotwire_livereload_received_default,
|
709
710
|
connected() {
|
710
711
|
console.log("[Hotwire::Livereload] Websocket connected");
|
@@ -713,8 +714,14 @@
|
|
713
714
|
console.log("[Hotwire::Livereload] Websocket disconnected");
|
714
715
|
}
|
715
716
|
});
|
717
|
+
subscription = createSubscription();
|
716
718
|
document.addEventListener("turbo:load", () => {
|
717
719
|
hotwire_livereload_scroll_position_default.restore();
|
718
720
|
hotwire_livereload_scroll_position_default.remove();
|
721
|
+
if (subscription) {
|
722
|
+
consumer.subscriptions.remove(subscription);
|
723
|
+
subscription = null;
|
724
|
+
}
|
725
|
+
subscription = createSubscription();
|
719
726
|
});
|
720
727
|
})();
|
@@ -3,7 +3,9 @@ import received from "./lib/hotwire-livereload-received"
|
|
3
3
|
import scrollPosition from "./lib/hotwire-livereload-scroll-position"
|
4
4
|
|
5
5
|
const consumer = createConsumer()
|
6
|
-
|
6
|
+
let subscription = null
|
7
|
+
|
8
|
+
const createSubscription = () => consumer.subscriptions.create("Hotwire::Livereload::ReloadChannel", {
|
7
9
|
received,
|
8
10
|
|
9
11
|
connected() {
|
@@ -15,8 +17,16 @@ consumer.subscriptions.create("Hotwire::Livereload::ReloadChannel", {
|
|
15
17
|
},
|
16
18
|
})
|
17
19
|
|
20
|
+
subscription = createSubscription()
|
21
|
+
|
18
22
|
document.addEventListener("turbo:load", () => {
|
19
23
|
scrollPosition.restore()
|
20
24
|
scrollPosition.remove()
|
25
|
+
|
26
|
+
if (subscription) {
|
27
|
+
consumer.subscriptions.remove(subscription)
|
28
|
+
subscription = null
|
29
|
+
}
|
30
|
+
subscription = createSubscription()
|
21
31
|
})
|
22
32
|
|
@@ -8,6 +8,7 @@ module Hotwire
|
|
8
8
|
isolate_namespace Hotwire::Livereload
|
9
9
|
config.hotwire_livereload = ActiveSupport::OrderedOptions.new
|
10
10
|
config.hotwire_livereload.listen_paths ||= []
|
11
|
+
config.hotwire_livereload.skip_listen_paths ||= []
|
11
12
|
config.hotwire_livereload.force_reload_paths ||= []
|
12
13
|
config.hotwire_livereload.reload_method = :action_cable
|
13
14
|
config.hotwire_livereload.disable_default_listeners = false
|
@@ -16,6 +17,7 @@ module Hotwire
|
|
16
17
|
#{root}/app/helpers
|
17
18
|
]
|
18
19
|
config.hotwire_livereload.listen_options ||= {}
|
20
|
+
config.hotwire_livereload.debounce_delay_ms = 0
|
19
21
|
|
20
22
|
initializer "hotwire_livereload.assets" do
|
21
23
|
if Rails.application.config.respond_to?(:assets)
|
@@ -31,6 +33,7 @@ module Hotwire
|
|
31
33
|
|
32
34
|
initializer "hotwire_livereload.set_configs" do |app|
|
33
35
|
options = app.config.hotwire_livereload
|
36
|
+
skip_listen_paths = options.skip_listen_paths.map(&:to_s).uniq
|
34
37
|
|
35
38
|
unless options.disable_default_listeners
|
36
39
|
default_listen_paths = %w[
|
@@ -55,11 +58,20 @@ module Hotwire
|
|
55
58
|
.uniq
|
56
59
|
.map { |p| Rails.root.join(p) }
|
57
60
|
.select { |p| Dir.exist?(p) }
|
61
|
+
.reject { |p| skip_listen_paths.include?(p.to_s) }
|
58
62
|
end
|
59
63
|
end
|
60
64
|
|
61
65
|
config.after_initialize do |app|
|
62
66
|
if Rails.env.development? && Hotwire::Livereload.server_process?
|
67
|
+
@trigger_reload = (Hotwire::Livereload.debounce(config.hotwire_livereload.debounce_delay_ms) do |options|
|
68
|
+
if config.hotwire_livereload.reload_method == :turbo_stream
|
69
|
+
Hotwire::Livereload.turbo_stream(options)
|
70
|
+
else
|
71
|
+
Hotwire::Livereload.action_cable(options)
|
72
|
+
end
|
73
|
+
end)
|
74
|
+
|
63
75
|
options = app.config.hotwire_livereload
|
64
76
|
listen_paths = options.listen_paths.map(&:to_s).uniq
|
65
77
|
force_reload_paths = options.force_reload_paths.map(&:to_s).uniq.join("|")
|
@@ -74,11 +86,7 @@ module Hotwire
|
|
74
86
|
end
|
75
87
|
|
76
88
|
options = {changed: changed, force_reload: force_reload}
|
77
|
-
|
78
|
-
Hotwire::Livereload.turbo_stream(options)
|
79
|
-
else
|
80
|
-
Hotwire::Livereload.action_cable(options)
|
81
|
-
end
|
89
|
+
@trigger_reload.call(options)
|
82
90
|
end
|
83
91
|
end
|
84
92
|
@listener.start
|
@@ -111,5 +119,27 @@ module Hotwire
|
|
111
119
|
|
112
120
|
puma_process || rails_server
|
113
121
|
end
|
122
|
+
|
123
|
+
def self.debounce(wait_ms, &block)
|
124
|
+
if wait_ms.zero?
|
125
|
+
return ->(*args) { yield(*args) }
|
126
|
+
end
|
127
|
+
|
128
|
+
mutex = Mutex.new
|
129
|
+
timer_thread = nil
|
130
|
+
seconds = wait_ms.to_f / 1000.0
|
131
|
+
|
132
|
+
lambda do |*args|
|
133
|
+
mutex.synchronize do
|
134
|
+
# Cancel previous timer
|
135
|
+
timer_thread&.kill
|
136
|
+
|
137
|
+
timer_thread = Thread.new do
|
138
|
+
sleep(seconds)
|
139
|
+
yield(*args)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
114
144
|
end
|
115
145
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hotwire-livereload
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kirill Platonov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-09-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: railties
|