Skip to content

Commit 82e4432

Browse files
author
David Heinemeier Hansson
authored
Javascript generator option with choices (rails#43160)
* Switch to a single controller option for choosing JavaScript approach * Remove remnants of webpacker specific work within Rails * No longer used * Missing space * Raise if unknown option is passed * Style * Use latest versions * Make channels setup generic to all node setups * Make Action Text installer work with any node package manager * Explaining variables are not useless * Rubocop pleasing * Don't rely on Rails.root Tests don't like it! * Rubocopping * Assume importmap * No longer relevant * Another cop * Style * Correct installation notice * Add dependencies for action cable when adding a channel * Fix paths to be relative to generator * Just go straight to yarn, forget about binstub * Fix tests * Fixup installer, only yarn once * Test generically with run * Style * Fix reference and reversibility * Style * Fix test * Test pinning dependencies * Remove extra space * Add more tests * Use latest dependencies * Relegated this to controllers * Refactor ChannelGenerator + more tests Use a uniform level of abstraction
1 parent 0b15b7d commit 82e4432

File tree

17 files changed

+250
-325
lines changed

17 files changed

+250
-325
lines changed

.rubocop.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,9 +218,6 @@ Lint/RedundantStringCoercion:
218218
Lint/UriEscapeUnescape:
219219
Enabled: true
220220

221-
Lint/UselessAssignment:
222-
Enabled: true
223-
224221
Lint/DeprecatedClassMethods:
225222
Enabled: true
226223

Gemfile.lock

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ GEM
286286
image_processing (1.12.1)
287287
mini_magick (>= 4.9.5, < 5)
288288
ruby-vips (>= 2.0.17, < 3)
289-
importmap-rails (0.3.4)
289+
importmap-rails (0.5.0)
290290
rails (>= 6.0.0)
291291
jmespath (1.4.0)
292292
json (2.5.1)
@@ -467,7 +467,7 @@ GEM
467467
sprockets (>= 3.0.0)
468468
sqlite3 (1.4.2)
469469
stackprof (0.2.17)
470-
stimulus-rails (0.3.9)
470+
stimulus-rails (0.4.0)
471471
rails (>= 6.0.0)
472472
sucker_punch (3.0.1)
473473
concurrent-ruby (~> 1.0)
@@ -480,7 +480,7 @@ GEM
480480
thor (1.1.0)
481481
tilt (2.0.10)
482482
trailblazer-option (0.1.1)
483-
turbo-rails (0.7.4)
483+
turbo-rails (0.7.10)
484484
rails (>= 6.0.0)
485485
tzinfo (2.0.4)
486486
concurrent-ruby (~> 1.0)

actioncable/lib/rails/generators/channel/channel_generator.rb

Lines changed: 79 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -13,51 +13,98 @@ class ChannelGenerator < NamedBase
1313

1414
hook_for :test_framework
1515

16-
def create_channel_file
17-
template "channel.rb", File.join("app/channels", class_path, "#{file_name}_channel.rb")
18-
19-
if options[:assets]
20-
if behavior == :invoke
21-
if defined?(Webpacker::Engine)
22-
template "javascript/index.js", "#{Webpacker.config.source_path}/channels/index.js"
23-
template "javascript/consumer.js", "#{Webpacker.config.source_path}/channels/consumer.js"
24-
else
25-
template "javascript/consumer.js", "app/javascript/channels/consumer.js"
26-
end
27-
end
16+
def create_channel_files
17+
create_shared_channel_files
18+
create_channel_file
2819

29-
if defined?(Webpacker::Engine)
30-
js_template "javascript/channel", File.join(Webpacker.config.source_path, "channels", class_path, "#{file_name}_channel")
31-
else
32-
channel_js_path = File.join("app/javascript/channels", class_path, "#{file_name}_channel")
33-
js_template "javascript/channel", channel_js_path
34-
gsub_file "#{channel_js_path}.js", /\.\/consumer/, "channels/consumer"
20+
if using_javascript?
21+
if first_setup_required?
22+
create_shared_channel_javascript_files
23+
import_channels_in_javascript_entrypoint
3524

36-
append_to_file "app/javascript/application.js", %(\nimport "channels/#{file_name}_channel"\n)
25+
if using_importmap?
26+
pin_javascript_dependencies
27+
elsif using_node?
28+
install_javascript_dependencies
29+
end
3730
end
38-
end
3931

40-
generate_application_cable_files
32+
create_channel_javascript_file
33+
import_channel_in_javascript_entrypoint
34+
end
4135
end
4236

4337
private
38+
def create_shared_channel_files
39+
return if behavior != :invoke
40+
41+
copy_file "#{__dir__}/templates/application_cable/channel.rb",
42+
"app/channels/application_cable/channel.rb"
43+
copy_file "#{__dir__}/templates/application_cable/connection.rb",
44+
"app/channels/application_cable/connection.rb"
45+
end
46+
47+
def create_channel_file
48+
template "channel.rb",
49+
File.join("app/channels", class_path, "#{file_name}_channel.rb")
50+
end
51+
52+
def create_shared_channel_javascript_files
53+
template "javascript/index.js", "app/javascript/channels/index.js"
54+
template "javascript/consumer.js", "app/javascript/channels/consumer.js"
55+
end
56+
57+
def create_channel_javascript_file
58+
channel_js_path = File.join("app/javascript/channels", class_path, "#{file_name}_channel")
59+
js_template "javascript/channel", channel_js_path
60+
gsub_file "#{channel_js_path}.js", /\.\/consumer/, "channels/consumer" unless using_node?
61+
end
62+
63+
def import_channels_in_javascript_entrypoint
64+
append_to_file "app/javascript/application.js",
65+
using_node? ? %(import "./channels"\n) : %(import "channels"\n)
66+
end
67+
68+
def import_channel_in_javascript_entrypoint
69+
append_to_file "app/javascript/channels/index.js",
70+
using_node? ? %(import "./#{file_name}_channel"\n) : %(import "channels/#{file_name}_channel"\n)
71+
end
72+
73+
def install_javascript_dependencies
74+
say "Installing JavaScript dependencies", :green
75+
run "yarn add @rails/actioncable"
76+
end
77+
78+
def pin_javascript_dependencies
79+
append_to_file "config/importmap.rb", <<-RUBY
80+
pin "@rails/actioncable", to: "actioncable.esm.js"
81+
pin_all_from "app/javascript/channels", under: "channels"
82+
RUBY
83+
end
84+
85+
4486
def file_name
4587
@_file_name ||= super.sub(/_channel\z/i, "")
4688
end
4789

48-
# FIXME: Change these files to symlinks once RubyGems 2.5.0 is required.
49-
def generate_application_cable_files
50-
return if behavior != :invoke
90+
def first_setup_required?
91+
!root.join("app/javascript/channels/index.js").exist?
92+
end
5193

52-
files = [
53-
"application_cable/channel.rb",
54-
"application_cable/connection.rb"
55-
]
94+
def using_javascript?
95+
@using_javascript ||= options[:assets] && root.join("app/javascript").exist?
96+
end
5697

57-
files.each do |name|
58-
path = File.join("app/channels/", name)
59-
template(name, path) if !File.exist?(path)
60-
end
98+
def using_node?
99+
@using_node ||= root.join("package.json").exist?
100+
end
101+
102+
def using_importmap?
103+
@using_importmap ||= root.join("config/importmap.rb").exist?
104+
end
105+
106+
def root
107+
@root ||= Pathname(destination_root)
61108
end
62109
end
63110
end
Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1 @@
1-
// Load all the channels within this directory and all subdirectories.
2-
// Channel files must be named *_channel.js.
3-
4-
const channels = require.context('.', true, /_channel\.js$/)
5-
channels.keys().forEach(channels)
1+
// Import all the channels to be used by Action Cable

actiontext/lib/generators/action_text/install/install_generator.rb

Lines changed: 16 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -9,70 +9,42 @@ class InstallGenerator < ::Rails::Generators::Base
99
source_root File.expand_path("templates", __dir__)
1010

1111
def install_javascript_dependencies
12-
if defined?(Webpacker::Engine)
12+
if using_node = Pathname(destination_root).join("package.json").exist?
1313
say "Installing JavaScript dependencies", :green
14-
yarn_command "add #{js_dependencies.map { |name, version| "#{name}@#{version}" }.join(" ")}"
14+
run "yarn add @rails/actiontext trix"
1515
end
1616
end
1717

1818
def append_javascript_dependencies
19-
if defined?(Webpacker::Engine)
20-
if (app_javascript_pack_path = Pathname.new("#{Webpacker.config.source_entry_path}/application.js")).exist?
21-
js_dependencies.each_key do |dependency|
22-
line = %[import "#{dependency}"]
19+
destination = Pathname(destination_root)
2320

24-
unless app_javascript_pack_path.read.include? line
25-
say "Adding #{dependency} to #{app_javascript_pack_path}", :green
26-
append_to_file app_javascript_pack_path, "\n#{line}"
27-
end
28-
end
29-
else
30-
say <<~WARNING, :red
31-
WARNING: Action Text can't locate your JavaScript bundle to add its package dependencies.
32-
33-
Add these lines to any bundles:
34-
35-
import "trix"
36-
import "@rails/actiontext"
37-
38-
Alternatively, install and setup the webpacker gem then rerun `bin/rails action_text:install`
39-
to have these dependencies added automatically.
40-
WARNING
41-
end
21+
if (application_javascript_path = destination.join("app/javascript/application.js")).exist?
22+
insert_into_file application_javascript_path.to_s, %(import "trix"\nimport "@rails/actiontext"\n)
4223
else
43-
if (application_javascript_path = Rails.root.join("app/javascript/application.js")).exist?
44-
insert_into_file application_javascript_path.to_s, %(\nimport "trix"\nimport "@rails/actiontext")
45-
else
46-
say <<~INSTRUCTIONS, :green
47-
You must import the @rails/actiontext and trix JavaScript modules in your application entrypoint.
48-
INSTRUCTIONS
49-
end
24+
say <<~INSTRUCTIONS, :green
25+
You must import the @rails/actiontext and trix JavaScript modules in your application entrypoint.
26+
INSTRUCTIONS
27+
end
5028

51-
if (importmap_path = Rails.root.join("config/importmap.rb")).exist?
52-
insert_into_file \
53-
importmap_path.to_s,
54-
%( pin "trix"\n pin "@rails/actiontext", to: "actiontext.js"\n\n),
55-
after: "Rails.application.config.importmap.draw do\n"
56-
else
57-
say <<~INSTRUCTIONS, :green
58-
You must add @rails/actiontext and trix to your importmap to reference them via ESM.
59-
INSTRUCTIONS
60-
end
29+
if (importmap_path = destination.join("config/importmap.rb")).exist?
30+
append_to_file importmap_path.to_s, %(pin "trix"\npin "@rails/actiontext", to: "actiontext.js"\n)
6131
end
6232
end
6333

6434
def create_actiontext_files
6535
template "actiontext.css", "app/assets/stylesheets/actiontext.css"
6636

67-
copy_file "#{GEM_ROOT}/app/views/active_storage/blobs/_blob.html.erb",
37+
gem_root = "#{__dir__}/../../../.."
38+
39+
copy_file "#{gem_root}/app/views/active_storage/blobs/_blob.html.erb",
6840
"app/views/active_storage/blobs/_blob.html.erb"
6941

70-
copy_file "#{GEM_ROOT}/app/views/layouts/action_text/contents/_content.html.erb",
42+
copy_file "#{gem_root}/app/views/layouts/action_text/contents/_content.html.erb",
7143
"app/views/layouts/action_text/contents/_content.html.erb"
7244
end
7345

7446
def enable_image_processing_gem
75-
if (gemfile_path = Rails.root.join("Gemfile")).exist?
47+
if (gemfile_path = Pathname(destination_root).join("Gemfile")).exist?
7648
say "Ensure image_processing gem has been enabled so image uploads will work (remember to bundle!)"
7749
uncomment_lines gemfile_path, /gem "image_processing"/
7850
end
@@ -83,19 +55,6 @@ def create_migrations
8355
end
8456

8557
hook_for :test_framework
86-
87-
private
88-
GEM_ROOT = "#{__dir__}/../../../.."
89-
90-
def js_dependencies
91-
js_package = JSON.load(Pathname.new("#{GEM_ROOT}/package.json"))
92-
js_package["peerDependencies"].merge \
93-
js_package["name"] => "^#{js_package["version"]}"
94-
end
95-
96-
def yarn_command(command, config = {})
97-
in_root { run "#{Thor::Util.ruby_command} bin/yarn #{command}", abort_on_failure: true, **config }
98-
end
9958
end
10059
end
10160
end

activestorage/README.md

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -148,13 +148,10 @@ Active Storage, with its included JavaScript library, supports uploading directl
148148
```html
149149
<%= javascript_include_tag "activestorage" %>
150150
```
151-
Requiring via importmap (as used by Stimulus) without bundling through the asset pipeline in the application html without autostart as ESM:
152-
```js
153-
{
154-
"imports": {
155-
"@rails/activestorage": "<%= asset_path "activestorage.esm" %>"
156-
}
157-
}
151+
Requiring via importmap-rails without bundling through the asset pipeline in the application html without autostart as ESM:
152+
```ruby
153+
# config/importmap.rb
154+
pin "@rails/activestorage", to: "activestorage.esm.js"
158155
```
159156
```html
160157
<script type="module-shim">

railties/lib/rails/app_updater.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ def generator_options
2727
options[:skip_action_cable] = !defined?(ActionCable::Engine)
2828
options[:skip_sprockets] = !defined?(Sprockets::Railtie)
2929
options[:skip_bootsnap] = !defined?(Bootsnap)
30-
options[:webpack] = File.exist?(Rails.root.join("config", "webpacker.yml"))
3130
options[:updating] = true
3231
options
3332
end

0 commit comments

Comments
 (0)