diff --git a/lib/generators/stimulus/stimulus_generator.rb b/lib/generators/stimulus/stimulus_generator.rb index 5af2437..ccf71a5 100644 --- a/lib/generators/stimulus/stimulus_generator.rb +++ b/lib/generators/stimulus/stimulus_generator.rb @@ -6,17 +6,22 @@ class StimulusGenerator < Rails::Generators::NamedBase # :nodoc: class_option :skip_manifest, type: :boolean, default: false, desc: "Don't update the stimulus manifest" def copy_view_files - @attribute = stimulus_attribute_value(controller_name) - template "controller.js", "app/javascript/controllers/#{controller_name}_controller.js" + @attribute = stimulus_attribute_value(File.join(class_path, file_name)) + template "controller.js", File.join("app/javascript/controllers", class_path, "#{file_name}_controller.js") rails_command "stimulus:manifest:update" unless Rails.root.join("config/importmap.rb").exist? || options[:skip_manifest] end private - def controller_name - name.underscore.gsub(/_controller$/, "") + + def file_name + @_file_name ||= remove_possible_suffix(super) + end + + def remove_possible_suffix(name) + name.sub(/_?controller$/i, "") end def stimulus_attribute_value(controller_name) - controller_name.gsub(/\//, "--").gsub("_", "-") + controller_name.sub(/\A\/+/, "").sub(/\/+\z/, "").gsub(/\//, "--").gsub("_", "-") end end diff --git a/lib/install/app/javascript/controllers/hello_controller.js b/lib/install/app/javascript/controllers/hello_controller.js deleted file mode 100644 index 5975c07..0000000 --- a/lib/install/app/javascript/controllers/hello_controller.js +++ /dev/null @@ -1,7 +0,0 @@ -import { Controller } from "@hotwired/stimulus" - -export default class extends Controller { - connect() { - this.element.textContent = "Hello World!" - } -} diff --git a/lib/install/app/javascript/controllers/index_for_node.js b/lib/install/app/javascript/controllers/index_for_node.js deleted file mode 100644 index d0685d3..0000000 --- a/lib/install/app/javascript/controllers/index_for_node.js +++ /dev/null @@ -1,8 +0,0 @@ -// This file is auto-generated by ./bin/rails stimulus:manifest:update -// Run that command whenever you add a new controller or create them with -// ./bin/rails generate stimulus controllerName - -import { application } from "./application" - -import HelloController from "./hello_controller" -application.register("hello", HelloController) diff --git a/lib/install/stimulus_with_bun.rb b/lib/install/stimulus_with_bun.rb index e6250b8..e716530 100644 --- a/lib/install/stimulus_with_bun.rb +++ b/lib/install/stimulus_with_bun.rb @@ -1,18 +1,19 @@ -say "Create controllers directory" -empty_directory "app/javascript/controllers" -copy_file "#{__dir__}/app/javascript/controllers/index_for_node.js", - "app/javascript/controllers/index.js" -copy_file "#{__dir__}/app/javascript/controllers/application.js", - "app/javascript/controllers/application.js" -copy_file "#{__dir__}/app/javascript/controllers/hello_controller.js", - "app/javascript/controllers/hello_controller.js" +say "Installing Stimulus (for bun) into #{destination_root}" +inside destination_root do + say "Create controllers directory" + empty_directory "app/javascript/controllers" + copy_file "#{__dir__}/app/javascript/controllers/application.js", "app/javascript/controllers/application.js" + run "rails generate stimulus hello --without-manifest" + run "rails stimulus:manifest:update" -if (Rails.root.join("app/javascript/application.js")).exist? - say "Import Stimulus controllers" - append_to_file "app/javascript/application.js", %(import "./controllers"\n) -else - say %(Couldn't find "app/javascript/application.js".\nYou must import "./controllers" in your JavaScript entrypoint file), :red -end + if File.exist?("app/javascript/application.js") + say "Import Stimulus controllers" + append_to_file "app/javascript/application.js", %(import "./controllers"\n) + else + say %(Couldn't find "app/javascript/application.js".), :red + say %(You must import "./controllers" in your JavaScript entrypoint file), :red + end -say "Install Stimulus" -run "bun add @hotwired/stimulus" + say "Install Stimulus" + run "bun add @hotwired/stimulus" +end diff --git a/lib/install/stimulus_with_importmap.rb b/lib/install/stimulus_with_importmap.rb index 170ee38..886da54 100644 --- a/lib/install/stimulus_with_importmap.rb +++ b/lib/install/stimulus_with_importmap.rb @@ -1,22 +1,27 @@ -say "Create controllers directory" -empty_directory "app/javascript/controllers" -copy_file "#{__dir__}/app/javascript/controllers/index_for_importmap.js", - "app/javascript/controllers/index.js" -copy_file "#{__dir__}/app/javascript/controllers/application.js", - "app/javascript/controllers/application.js" -copy_file "#{__dir__}/app/javascript/controllers/hello_controller.js", - "app/javascript/controllers/hello_controller.js" +say "Installing Stimulus (for importmap-rails) into #{destination_root}" +inside destination_root do + say "Create controllers directory" + empty_directory "app/javascript/controllers" + copy_file "#{__dir__}/app/javascript/controllers/index_for_importmap.js", "app/javascript/controllers/index.js" + copy_file "#{__dir__}/app/javascript/controllers/application.js", "app/javascript/controllers/application.js" + run "rails generate stimulus hello --without-manifest" -say "Import Stimulus controllers" -append_to_file "app/javascript/application.js", %(import "controllers"\n) + if File.exist?("app/javascript/application.js") + say "Import Stimulus controllers" + append_to_file "app/javascript/application.js", %(import "controllers"\n) + else + say %(Couldn't find "app/javascript/application.js".), :red + say %(You must import "controllers" in your JavaScript entrypoint file), :red + end -say "Pin Stimulus" -say %(Appending: pin "@hotwired/stimulus", to: "stimulus.min.js") -append_to_file "config/importmap.rb", %(pin "@hotwired/stimulus", to: "stimulus.min.js"\n) + say "Pin Stimulus" + say %(Appending: pin "@hotwired/stimulus", to: "stimulus.min.js") + append_to_file "config/importmap.rb", %(pin "@hotwired/stimulus", to: "stimulus.min.js"\n) -say %(Appending: pin "@hotwired/stimulus-loading", to: "stimulus-loading.js") -append_to_file "config/importmap.rb", %(pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"\n) + say %(Appending: pin "@hotwired/stimulus-loading", to: "stimulus-loading.js") + append_to_file "config/importmap.rb", %(pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"\n) -say "Pin all controllers" -say %(Appending: pin_all_from "app/javascript/controllers", under: "controllers") -append_to_file "config/importmap.rb", %(pin_all_from "app/javascript/controllers", under: "controllers"\n) + say "Pin all controllers" + say %(Appending: pin_all_from "#{javascript_root}/controllers", under: "controllers") + append_to_file "config/importmap.rb", %(pin_all_from "#{javascript_root}/controllers", under: "controllers"\n) +end diff --git a/lib/install/stimulus_with_node.rb b/lib/install/stimulus_with_node.rb index 50431dc..e7bac13 100644 --- a/lib/install/stimulus_with_node.rb +++ b/lib/install/stimulus_with_node.rb @@ -1,22 +1,19 @@ -say "Create controllers directory" -empty_directory "app/javascript/controllers" -copy_file "#{__dir__}/app/javascript/controllers/index_for_node.js", - "app/javascript/controllers/index.js" -copy_file "#{__dir__}/app/javascript/controllers/application.js", - "app/javascript/controllers/application.js" -copy_file "#{__dir__}/app/javascript/controllers/hello_controller.js", - "app/javascript/controllers/hello_controller.js" +say "Installing Stimulus (for node) into #{destination_root}" +inside destination_root do + say "Create controllers directory" + empty_directory "app/javascript/controllers" + copy_file "#{__dir__}/app/javascript/controllers/application.js", "app/javascript/controllers/application.js" + run "rails generate stimulus hello --without-manifest" + run "rails stimulus:manifest:update" -if (Rails.root.join("app/javascript/application.js")).exist? - say "Import Stimulus controllers" - append_to_file "app/javascript/application.js", %(import "./controllers"\n) -else - say %(Couldn't find "app/javascript/application.js".\nYou must import "./controllers" in your JavaScript entrypoint file), :red -end + if File.exist?("app/javascript/application.js") + say "Import Stimulus controllers" + append_to_file "app/javascript/application.js", %(import "./controllers"\n) + else + say %(Couldn't find "app/javascript/application.js".), :red + say %(You must import "./controllers" in your JavaScript entrypoint file), :red + end -say "Install Stimulus" -if (Rails.root.join("bun.config.js")).exist? - run "bun add @hotwired/stimulus" -else + say "Install Stimulus" run "yarn add @hotwired/stimulus" end diff --git a/lib/rake/extensions.rb b/lib/rake/extensions.rb new file mode 100644 index 0000000..ad643fd --- /dev/null +++ b/lib/rake/extensions.rb @@ -0,0 +1,23 @@ +# Extend Rake to allow unscoping from within a scope/namespace. +# This allows engine tasks to unscope from :app and define top-level tasks. +module Rake + + module TaskManager + def unscoped + current_scope = @scope + @scope = Scope.make + ns = NameSpace.new(self, @scope) + yield(ns) + ns + ensure + @scope = current_scope + end + end + + module DSL + def unscoped(&block) + Rake.application.unscoped(&block) + end + end + +end \ No newline at end of file diff --git a/lib/tasks/stimulus_tasks.rake b/lib/tasks/stimulus_tasks.rake index ec08183..ae5e247 100644 --- a/lib/tasks/stimulus_tasks.rake +++ b/lib/tasks/stimulus_tasks.rake @@ -1,46 +1,55 @@ require "stimulus/manifest" +require "rake/extensions" +require "rails/generators" +require "rails/generators/rails/app/app_generator" module Stimulus module Tasks extend self - def run_stimulus_install_template(path) - system RbConfig.ruby, "./bin/rails", "app:template", "LOCATION=#{File.expand_path("../install/#{path}.rb", __dir__)}" + + def detect_install_template(root) + if root.join("config/importmap.rb").exist? + "importmap" + elsif root.join("package.json").exist? + if root.join("bun.config.js").exist? + "bun" + else + "node" + end + end end - def using_bun? - Rails.root.join("bun.config.js").exist? + def self.run_stimulus_install_template(path, root) + Rails::Generators::AppGenerator.apply_rails_template(File.expand_path("../install/#{path}.rb", __dir__), root) end + end end namespace :stimulus do desc "Install Stimulus into the app" task :install do - if Rails.root.join("config/importmap.rb").exist? - Rake::Task["stimulus:install:importmap"].invoke - elsif Rails.root.join("package.json").exist? && Stimulus::Tasks.using_bun? - Rake::Task["stimulus:install:bun"].invoke - elsif Rails.root.join("package.json").exist? - Rake::Task["stimulus:install:node"].invoke + if (install_template = Stimulus::Tasks.detect_install_template(Rails.root)) + Stimulus::Tasks.run_stimulus_install_template("stimulus_with_#{install_template}", Rails.root) else puts "You must either be running with node (package.json) or importmap-rails (config/importmap.rb) to use this gem." end end namespace :install do - desc "Install Stimulus on an app running importmap-rails" + desc "Install Stimulus in an app running importmap-rails" task :importmap do - Stimulus::Tasks.run_stimulus_install_template "stimulus_with_importmap" + Stimulus::Tasks.run_stimulus_install_template("stimulus_with_importmap", Rails.root) end - desc "Install Stimulus on an app running node" + desc "Install Stimulus in an app running node" task :node do - Stimulus::Tasks.run_stimulus_install_template "stimulus_with_node" + Stimulus::Tasks.run_stimulus_install_template("stimulus_with_node", Rails.root) end - desc "Install Stimulus on an app running bun" + desc "Install Stimulus in an app running bun" task :bun do - Stimulus::Tasks.run_stimulus_install_template "stimulus_with_bun" + Stimulus::Tasks.run_stimulus_install_template("stimulus_with_bun", Rails.root) end end @@ -52,8 +61,7 @@ namespace :stimulus do desc "Update the Stimulus manifest (will overwrite controllers/index.js)" task :update do - manifest = - Stimulus::Manifest.generate_from(Rails.root.join("app/javascript/controllers")) + manifest = Stimulus::Manifest.generate_from(Rails.root.join("app/javascript/controllers")) File.open(Rails.root.join("app/javascript/controllers/index.js"), "w+") do |index| index.puts "// This file is auto-generated by ./bin/rails stimulus:manifest:update" @@ -66,3 +74,58 @@ namespace :stimulus do end end end + +if defined?(ENGINE_ROOT) && (engine = Rails::Engine.find(ENGINE_ROOT)) + unscoped do + namespace :stimulus do + + desc "Install Stimulus into the engine" + task :install do + if (install_template = Stimulus::Tasks.detect_install_template(engine.root)) + Stimulus::Tasks.run_stimulus_install_template("stimulus_with_#{install_template}", engine.root) + else + puts "You must either be running with node/bun (package.json) or importmap-rails (config/importmap.rb) to use this gem." + end + end + + namespace :install do + desc "Install Stimulus in an engine running importmap-rails" + task :importmap do + Stimulus::Tasks.run_stimulus_install_template("stimulus_with_importmap", Rails.root) + end + + desc "Install Stimulus in an engine running node" + task :node do + Stimulus::Tasks.run_stimulus_install_template("stimulus_with_node", Rails.root) + end + + desc "Install Stimulus in an engine running bun" + task :bun do + Stimulus::Tasks.run_stimulus_install_template("stimulus_with_bun", Rails.root) + end + end + + namespace :manifest do + + desc "Show the current engine Stimulus manifest (all installed controllers)" + task :display do + puts Stimulus::Manifest.generate_from(engine.root.join("app/javascript/controllers")) + end + + desc "Update the engine Stimulus manifest (will overwrite controllers/index.js)" + task :update do + manifest = Stimulus::Manifest.generate_from(engine.root.join("app/javascript/controllers")) + File.open(engine.root.join("app/javascript/controllers/index.js"), "w+") do |index| + index.puts "// This file is auto-generated by ./bin/rails stimulus:manifest:update" + index.puts "// Run that command whenever you add a new controller or create them with" + index.puts "// ./bin/rails generate stimulus controllerName" + index.puts + index.puts %(import { application } from "./application") + index.puts manifest + end + end + + end + end + end +end diff --git a/test/generator_test.rb b/test/generator_test.rb index df39713..df5e9a9 100644 --- a/test/generator_test.rb +++ b/test/generator_test.rb @@ -26,7 +26,6 @@ class StimulusGeneratorTest < Rails::Generators::TestCase assert_file "app/javascript/controllers/hello_world_controller.js", /data-controller="hello-world"/ end - test "generating with camelized name and lower case first letter" do run_generator ["helloWorld"] @@ -46,9 +45,9 @@ class StimulusGeneratorTest < Rails::Generators::TestCase end test "generating with namespaced name" do - run_generator ["hello/world"] + run_generator ["hello/happy/world"] - assert_file "app/javascript/controllers/hello/world_controller.js", /data-controller="hello--world"/ + assert_file "app/javascript/controllers/hello/happy/world_controller.js", /data-controller="hello--happy--world"/ end test "generating with namespaced and camelized name" do