diff --git a/.rubocop.yml b/.rubocop.yml index 66c4a087544..15a16a54c95 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -218,9 +218,6 @@ Lint/RedundantStringCoercion: Lint/UriEscapeUnescape: Enabled: true -Lint/UselessAssignment: - Enabled: true - Lint/DeprecatedClassMethods: Enabled: true diff --git a/Gemfile.lock b/Gemfile.lock index 1efec98c24a..c4b6bda56c8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -286,7 +286,7 @@ GEM image_processing (1.12.1) mini_magick (>= 4.9.5, < 5) ruby-vips (>= 2.0.17, < 3) - importmap-rails (0.3.4) + importmap-rails (0.5.0) rails (>= 6.0.0) jmespath (1.4.0) json (2.5.1) @@ -467,7 +467,7 @@ GEM sprockets (>= 3.0.0) sqlite3 (1.4.2) stackprof (0.2.17) - stimulus-rails (0.3.9) + stimulus-rails (0.4.0) rails (>= 6.0.0) sucker_punch (3.0.1) concurrent-ruby (~> 1.0) @@ -480,7 +480,7 @@ GEM thor (1.1.0) tilt (2.0.10) trailblazer-option (0.1.1) - turbo-rails (0.7.4) + turbo-rails (0.7.10) rails (>= 6.0.0) tzinfo (2.0.4) concurrent-ruby (~> 1.0) diff --git a/actioncable/lib/rails/generators/channel/channel_generator.rb b/actioncable/lib/rails/generators/channel/channel_generator.rb index ec342066e0e..a72dc0672f0 100644 --- a/actioncable/lib/rails/generators/channel/channel_generator.rb +++ b/actioncable/lib/rails/generators/channel/channel_generator.rb @@ -13,51 +13,98 @@ module Rails hook_for :test_framework - def create_channel_file - template "channel.rb", File.join("app/channels", class_path, "#{file_name}_channel.rb") + def create_channel_files + create_shared_channel_files + create_channel_file - if options[:assets] - if behavior == :invoke - if defined?(Webpacker::Engine) - template "javascript/index.js", "#{Webpacker.config.source_path}/channels/index.js" - template "javascript/consumer.js", "#{Webpacker.config.source_path}/channels/consumer.js" - else - template "javascript/consumer.js", "app/javascript/channels/consumer.js" + if using_javascript? + if first_setup_required? + create_shared_channel_javascript_files + import_channels_in_javascript_entrypoint + + if using_importmap? + pin_javascript_dependencies + elsif using_node? + install_javascript_dependencies end end - if defined?(Webpacker::Engine) - js_template "javascript/channel", File.join(Webpacker.config.source_path, "channels", class_path, "#{file_name}_channel") - else - channel_js_path = File.join("app/javascript/channels", class_path, "#{file_name}_channel") - js_template "javascript/channel", channel_js_path - gsub_file "#{channel_js_path}.js", /\.\/consumer/, "channels/consumer" - - append_to_file "app/javascript/application.js", %(\nimport "channels/#{file_name}_channel"\n) - end + create_channel_javascript_file + import_channel_in_javascript_entrypoint end - - generate_application_cable_files end private + def create_shared_channel_files + return if behavior != :invoke + + copy_file "#{__dir__}/templates/application_cable/channel.rb", + "app/channels/application_cable/channel.rb" + copy_file "#{__dir__}/templates/application_cable/connection.rb", + "app/channels/application_cable/connection.rb" + end + + def create_channel_file + template "channel.rb", + File.join("app/channels", class_path, "#{file_name}_channel.rb") + end + + def create_shared_channel_javascript_files + template "javascript/index.js", "app/javascript/channels/index.js" + template "javascript/consumer.js", "app/javascript/channels/consumer.js" + end + + def create_channel_javascript_file + channel_js_path = File.join("app/javascript/channels", class_path, "#{file_name}_channel") + js_template "javascript/channel", channel_js_path + gsub_file "#{channel_js_path}.js", /\.\/consumer/, "channels/consumer" unless using_node? + end + + def import_channels_in_javascript_entrypoint + append_to_file "app/javascript/application.js", + using_node? ? %(import "./channels"\n) : %(import "channels"\n) + end + + def import_channel_in_javascript_entrypoint + append_to_file "app/javascript/channels/index.js", + using_node? ? %(import "./#{file_name}_channel"\n) : %(import "channels/#{file_name}_channel"\n) + end + + def install_javascript_dependencies + say "Installing JavaScript dependencies", :green + run "yarn add @rails/actioncable" + end + + def pin_javascript_dependencies + append_to_file "config/importmap.rb", <<-RUBY +pin "@rails/actioncable", to: "actioncable.esm.js" +pin_all_from "app/javascript/channels", under: "channels" + RUBY + end + + def file_name @_file_name ||= super.sub(/_channel\z/i, "") end - # FIXME: Change these files to symlinks once RubyGems 2.5.0 is required. - def generate_application_cable_files - return if behavior != :invoke + def first_setup_required? + !root.join("app/javascript/channels/index.js").exist? + end - files = [ - "application_cable/channel.rb", - "application_cable/connection.rb" - ] + def using_javascript? + @using_javascript ||= options[:assets] && root.join("app/javascript").exist? + end - files.each do |name| - path = File.join("app/channels/", name) - template(name, path) if !File.exist?(path) - end + def using_node? + @using_node ||= root.join("package.json").exist? + end + + def using_importmap? + @using_importmap ||= root.join("config/importmap.rb").exist? + end + + def root + @root ||= Pathname(destination_root) end end end diff --git a/actioncable/lib/rails/generators/channel/templates/application_cable/channel.rb.tt b/actioncable/lib/rails/generators/channel/templates/application_cable/channel.rb similarity index 100% rename from actioncable/lib/rails/generators/channel/templates/application_cable/channel.rb.tt rename to actioncable/lib/rails/generators/channel/templates/application_cable/channel.rb diff --git a/actioncable/lib/rails/generators/channel/templates/application_cable/connection.rb.tt b/actioncable/lib/rails/generators/channel/templates/application_cable/connection.rb similarity index 100% rename from actioncable/lib/rails/generators/channel/templates/application_cable/connection.rb.tt rename to actioncable/lib/rails/generators/channel/templates/application_cable/connection.rb diff --git a/actioncable/lib/rails/generators/channel/templates/javascript/index.js.tt b/actioncable/lib/rails/generators/channel/templates/javascript/index.js.tt index 0cfcf749196..08dc8af2a03 100644 --- a/actioncable/lib/rails/generators/channel/templates/javascript/index.js.tt +++ b/actioncable/lib/rails/generators/channel/templates/javascript/index.js.tt @@ -1,5 +1 @@ -// Load all the channels within this directory and all subdirectories. -// Channel files must be named *_channel.js. - -const channels = require.context('.', true, /_channel\.js$/) -channels.keys().forEach(channels) +// Import all the channels to be used by Action Cable diff --git a/actiontext/lib/generators/action_text/install/install_generator.rb b/actiontext/lib/generators/action_text/install/install_generator.rb index 33c38e61ee7..f86f23eaa01 100644 --- a/actiontext/lib/generators/action_text/install/install_generator.rb +++ b/actiontext/lib/generators/action_text/install/install_generator.rb @@ -9,70 +9,42 @@ module ActionText source_root File.expand_path("templates", __dir__) def install_javascript_dependencies - if defined?(Webpacker::Engine) + if using_node = Pathname(destination_root).join("package.json").exist? say "Installing JavaScript dependencies", :green - yarn_command "add #{js_dependencies.map { |name, version| "#{name}@#{version}" }.join(" ")}" + run "yarn add @rails/actiontext trix" end end def append_javascript_dependencies - if defined?(Webpacker::Engine) - if (app_javascript_pack_path = Pathname.new("#{Webpacker.config.source_entry_path}/application.js")).exist? - js_dependencies.each_key do |dependency| - line = %[import "#{dependency}"] + destination = Pathname(destination_root) - unless app_javascript_pack_path.read.include? line - say "Adding #{dependency} to #{app_javascript_pack_path}", :green - append_to_file app_javascript_pack_path, "\n#{line}" - end - end - else - say <<~WARNING, :red - WARNING: Action Text can't locate your JavaScript bundle to add its package dependencies. - - Add these lines to any bundles: - - import "trix" - import "@rails/actiontext" - - Alternatively, install and setup the webpacker gem then rerun `bin/rails action_text:install` - to have these dependencies added automatically. - WARNING - end + if (application_javascript_path = destination.join("app/javascript/application.js")).exist? + insert_into_file application_javascript_path.to_s, %(import "trix"\nimport "@rails/actiontext"\n) else - if (application_javascript_path = Rails.root.join("app/javascript/application.js")).exist? - insert_into_file application_javascript_path.to_s, %(\nimport "trix"\nimport "@rails/actiontext") - else - say <<~INSTRUCTIONS, :green - You must import the @rails/actiontext and trix JavaScript modules in your application entrypoint. - INSTRUCTIONS - end + say <<~INSTRUCTIONS, :green + You must import the @rails/actiontext and trix JavaScript modules in your application entrypoint. + INSTRUCTIONS + end - if (importmap_path = Rails.root.join("config/importmap.rb")).exist? - insert_into_file \ - importmap_path.to_s, - %( pin "trix"\n pin "@rails/actiontext", to: "actiontext.js"\n\n), - after: "Rails.application.config.importmap.draw do\n" - else - say <<~INSTRUCTIONS, :green - You must add @rails/actiontext and trix to your importmap to reference them via ESM. - INSTRUCTIONS - end + if (importmap_path = destination.join("config/importmap.rb")).exist? + append_to_file importmap_path.to_s, %(pin "trix"\npin "@rails/actiontext", to: "actiontext.js"\n) end end def create_actiontext_files template "actiontext.css", "app/assets/stylesheets/actiontext.css" - copy_file "#{GEM_ROOT}/app/views/active_storage/blobs/_blob.html.erb", + gem_root = "#{__dir__}/../../../.." + + copy_file "#{gem_root}/app/views/active_storage/blobs/_blob.html.erb", "app/views/active_storage/blobs/_blob.html.erb" - copy_file "#{GEM_ROOT}/app/views/layouts/action_text/contents/_content.html.erb", + copy_file "#{gem_root}/app/views/layouts/action_text/contents/_content.html.erb", "app/views/layouts/action_text/contents/_content.html.erb" end def enable_image_processing_gem - if (gemfile_path = Rails.root.join("Gemfile")).exist? + if (gemfile_path = Pathname(destination_root).join("Gemfile")).exist? say "Ensure image_processing gem has been enabled so image uploads will work (remember to bundle!)" uncomment_lines gemfile_path, /gem "image_processing"/ end @@ -83,19 +55,6 @@ module ActionText end hook_for :test_framework - - private - GEM_ROOT = "#{__dir__}/../../../.." - - def js_dependencies - js_package = JSON.load(Pathname.new("#{GEM_ROOT}/package.json")) - js_package["peerDependencies"].merge \ - js_package["name"] => "^#{js_package["version"]}" - end - - def yarn_command(command, config = {}) - in_root { run "#{Thor::Util.ruby_command} bin/yarn #{command}", abort_on_failure: true, **config } - end end end end diff --git a/activestorage/README.md b/activestorage/README.md index 0ded69c3681..67709c8c2d2 100644 --- a/activestorage/README.md +++ b/activestorage/README.md @@ -148,13 +148,10 @@ Active Storage, with its included JavaScript library, supports uploading directl ```html <%= javascript_include_tag "activestorage" %> ``` - Requiring via importmap (as used by Stimulus) without bundling through the asset pipeline in the application html without autostart as ESM: - ```js - { - "imports": { - "@rails/activestorage": "<%= asset_path "activestorage.esm" %>" - } - } + Requiring via importmap-rails without bundling through the asset pipeline in the application html without autostart as ESM: + ```ruby + # config/importmap.rb + pin "@rails/activestorage", to: "activestorage.esm.js" ``` ```html