Skip to content

Commit

Permalink
Merge pull request #34 from basecamp/hadnle-unregister-controllers
Browse files Browse the repository at this point in the history
Handle deletion of stimulus controllers
  • Loading branch information
jorgemanrubia authored Dec 20, 2024
2 parents 6537aff + ba2b81b commit dda43d8
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 19 deletions.
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
hotwire-spark (0.1.6)
hotwire-spark (0.1.7)
listen
rails (>= 8.0.0)
zeitwerk
Expand Down
30 changes: 26 additions & 4 deletions app/assets/javascripts/hotwire_spark.js
Original file line number Diff line number Diff line change
Expand Up @@ -3537,14 +3537,19 @@ var HotwireSpark = (function () {
async reload() {
log("Reload Stimulus controllers...");
this.application.stop();
await this.#reloadStimulusControllers();
await this.#reloadChangedStimulusControllers();
this.#unloadDeletedStimulusControllers();
this.application.start();
}
async #reloadStimulusControllers() {
await Promise.all(this.#stimulusControllerPaths.map(async moduleName => this.#reloadStimulusController(moduleName)));
async #reloadChangedStimulusControllers() {
await Promise.all(this.#stimulusControllerPathsToReload.map(async moduleName => this.#reloadStimulusController(moduleName)));
}
get #stimulusControllerPathsToReload() {
this.controllerPathsToReload = this.controllerPathsToReload || this.#stimulusControllerPaths.filter(path => this.#shouldReloadController(path));
return this.controllerPathsToReload;
}
get #stimulusControllerPaths() {
return Object.keys(this.#stimulusPathsByModule).filter(path => path.endsWith("_controller") && this.#shouldReloadController(path));
return Object.keys(this.#stimulusPathsByModule).filter(path => path.endsWith("_controller"));
}
#shouldReloadController(path) {
return this.filePattern.test(path);
Expand All @@ -3564,6 +3569,19 @@ var HotwireSpark = (function () {
const module = await import(path);
this.#registerController(controllerName, module);
}
#unloadDeletedStimulusControllers() {
this.#controllersToUnload.forEach(controller => this.#deregisterController(controller.identifier));
}
get #controllersToUnload() {
if (this.#didChangeTriggerAReload) {
return [];
} else {
return this.application.controllers.filter(controller => this.filePattern.test(`${controller.identifier}_controller`));
}
}
get #didChangeTriggerAReload() {
return this.#stimulusControllerPathsToReload.length > 0;
}
#pathForModuleName(moduleName) {
return this.#stimulusPathsByModule[moduleName];
}
Expand All @@ -3574,6 +3592,10 @@ var HotwireSpark = (function () {
this.application.unload(name);
this.application.register(name, module.default);
}
#deregisterController(name) {
log(`\tRemoving controller ${name}`);
this.application.unload(name);
}
}

class HtmlReloader {
Expand Down
2 changes: 1 addition & 1 deletion app/assets/javascripts/hotwire_spark.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion app/assets/javascripts/hotwire_spark.min.js.map

Large diffs are not rendered by default.

37 changes: 33 additions & 4 deletions app/javascript/hotwire/spark/reloaders/stimulus_reloader.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,26 @@ export class StimulusReloader {
log("Reload Stimulus controllers...")

this.application.stop()
await this.#reloadStimulusControllers()

await this.#reloadChangedStimulusControllers()
this.#unloadDeletedStimulusControllers()

this.application.start()
}

async #reloadStimulusControllers() {
async #reloadChangedStimulusControllers() {
await Promise.all(
this.#stimulusControllerPaths.map(async moduleName => this.#reloadStimulusController(moduleName))
this.#stimulusControllerPathsToReload.map(async moduleName => this.#reloadStimulusController(moduleName))
)
}

get #stimulusControllerPathsToReload() {
this.controllerPathsToReload = this.controllerPathsToReload || this.#stimulusControllerPaths.filter(path => this.#shouldReloadController(path))
return this.controllerPathsToReload
}

get #stimulusControllerPaths() {
return Object.keys(this.#stimulusPathsByModule).filter(path => path.endsWith("_controller") && this.#shouldReloadController(path))
return Object.keys(this.#stimulusPathsByModule).filter(path => path.endsWith("_controller"))
}

#shouldReloadController(path) {
Expand Down Expand Up @@ -57,6 +65,22 @@ export class StimulusReloader {
this.#registerController(controllerName, module)
}

#unloadDeletedStimulusControllers() {
this.#controllersToUnload.forEach(controller => this.#deregisterController(controller.identifier))
}

get #controllersToUnload() {
if (this.#didChangeTriggerAReload) {
return []
} else {
return this.application.controllers.filter(controller => this.filePattern.test(`${controller.identifier}_controller`))
}
}

get #didChangeTriggerAReload() {
return this.#stimulusControllerPathsToReload.length > 0
}

#pathForModuleName(moduleName) {
return this.#stimulusPathsByModule[moduleName]
}
Expand All @@ -73,4 +97,9 @@ export class StimulusReloader {
this.application.unload(name)
this.application.register(name, module.default)
}

#deregisterController(name) {
log(`\tRemoving controller ${name}`)
this.application.unload(name)
}
}
2 changes: 1 addition & 1 deletion lib/hotwire/spark/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module Hotwire
module Spark
VERSION = "0.1.6"
VERSION = "0.1.7"
end
end
2 changes: 2 additions & 0 deletions test/dummy/app/javascript/controllers/dummy_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ export default class extends Controller {
connect() {
console.debug("Dummy controller connected ", this.version)
this.element.querySelector("#replace").textContent = "_REPLACE_"
this.element.setAttribute("data-dummy-version", this.version)
}

disconnect() {
console.debug("Dummy controller disconnected", this.version)
this.element.removeAttribute("data-dummy-version")
}
}
28 changes: 23 additions & 5 deletions test/helpers/files_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ module FilesHelper
ORIGINAL_EXTENSION = ".original"

def edit_file(path, replace:, with:)
path = Rails.application.root.join(path).to_s
path = expand_path(path)

raise ArgumentError, "File at '#{path}' does not exist." unless File.exist?(path)

original_path = "#{path}#{ORIGINAL_EXTENSION}"
remember_path_to_restore original_path
system "cp", path, original_path
original_path = remember_original_path_to_restore path

FileUtils.cp path, original_path

content = File.read(path)
updated_content = content.gsub(replace, with)
Expand All @@ -27,7 +27,7 @@ def edit_file(path, replace:, with:)
end

def add_file(path, content)
path = Rails.application.root.join(path).to_s
path = expand_path(path)

raise ArgumentError, "File at '#{path}' already exists." if File.exist?(path)

Expand All @@ -37,7 +37,25 @@ def add_file(path, content)
reload_rails_reloader
end

def remove_file(path)
path = expand_path(path)

original_path = remember_original_path_to_restore path

FileUtils.mv path, original_path
end

private
def expand_path(path)
Rails.application.root.join(path).to_s
end

def remember_original_path_to_restore(path)
"#{path}#{ORIGINAL_EXTENSION}".tap do |original_path|
remember_path_to_restore original_path
end
end

def remember_path_to_restore(path)
paths_to_restore << path
end
Expand Down
14 changes: 12 additions & 2 deletions test/stimulus_reload_test.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
require "application_system_test_case"

class StimulusReloadTest < ApplicationSystemTestCase
test "reload Stimulus controller changes" do
setup do
visit root_path
end

test "reload Stimulus controller changes" do
assert_no_text "This was replaced!"

edit_file "app/javascript/controllers/dummy_controller.js", replace: "_REPLACE_", with: "This was replaced!"
Expand All @@ -11,7 +14,6 @@ class StimulusReloadTest < ApplicationSystemTestCase
end

test "load new Stimulus controllers" do
visit root_path
assert_no_text "This was replaced!"

edit_file "app/views/home/show.html.erb", replace: "_REPLACE_CONTROLLER_", with: "other-dummy"
Expand All @@ -29,4 +31,12 @@ class StimulusReloadTest < ApplicationSystemTestCase

assert_text "This was replaced!"
end

test "unload removed Stimulus controllers" do
assert_css "[data-dummy-version]"

remove_file "app/javascript/controllers/dummy_controller.js"

assert_no_css "[data-dummy-version]"
end
end

0 comments on commit dda43d8

Please sign in to comment.