mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Javascript generator option with choices (#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
This commit is contained in:
parent
0b15b7d59c
commit
82e4432058
17 changed files with 250 additions and 325 deletions
|
@ -218,9 +218,6 @@ Lint/RedundantStringCoercion:
|
|||
Lint/UriEscapeUnescape:
|
||||
Enabled: true
|
||||
|
||||
Lint/UselessAssignment:
|
||||
Enabled: true
|
||||
|
||||
Lint/DeprecatedClassMethods:
|
||||
Enabled: true
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
<script type="module-shim">
|
||||
|
|
|
@ -27,7 +27,6 @@ module Rails
|
|||
options[:skip_action_cable] = !defined?(ActionCable::Engine)
|
||||
options[:skip_sprockets] = !defined?(Sprockets::Railtie)
|
||||
options[:skip_bootsnap] = !defined?(Bootsnap)
|
||||
options[:webpack] = File.exist?(Rails.root.join("config", "webpacker.yml"))
|
||||
options[:updating] = true
|
||||
options
|
||||
end
|
||||
|
|
|
@ -108,8 +108,8 @@ module Rails
|
|||
[rails_gemfile_entry,
|
||||
database_gemfile_entry,
|
||||
web_server_gemfile_entry,
|
||||
webpacker_gemfile_entry,
|
||||
javascript_gemfile_entry,
|
||||
hotwire_gemfile_entry,
|
||||
jbuilder_gemfile_entry,
|
||||
psych_gemfile_entry,
|
||||
cable_gemfile_entry].flatten.find_all(&@gem_filter)
|
||||
|
@ -284,36 +284,6 @@ module Rails
|
|||
end
|
||||
end
|
||||
|
||||
# This "npm-ifies" the current version number
|
||||
# With npm, versions such as "5.0.0.rc1" or "5.0.0.beta1.1" are not compliant with its
|
||||
# versioning system, so they must be transformed to "5.0.0-rc1" and "5.0.0-beta1-1" respectively.
|
||||
#
|
||||
# "5.0.1" --> "5.0.1"
|
||||
# "5.0.1.1" --> "5.0.1-1" *
|
||||
# "5.0.0.rc1" --> "5.0.0-rc1"
|
||||
#
|
||||
# * This makes it a prerelease. That's bad, but we haven't come up with
|
||||
# a better solution at the moment.
|
||||
def npm_version
|
||||
if options.edge? || options.main? || options.dev?
|
||||
# TODO: ideally this would read from Github
|
||||
# https://github.com/rails/rails/blob/main/actioncable/app/assets/javascripts/action_cable.js
|
||||
# https://github.com/rails/rails/blob/main/activestorage/app/assets/javascripts/activestorage.js
|
||||
# https://github.com/rails/rails/tree/main/actionview/app/assets/javascripts -> not clear where the output file is
|
||||
"latest"
|
||||
else
|
||||
Rails.version.gsub(/\./).with_index { |s, i| i >= 2 ? "-" : s }
|
||||
end
|
||||
end
|
||||
|
||||
def webpacker_gemfile_entry
|
||||
if options[:webpack]
|
||||
GemfileEntry.version "webpacker", "~> 6.0.0.rc.5", "Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker"
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
def jbuilder_gemfile_entry
|
||||
return [] if options[:skip_jbuilder]
|
||||
comment = "Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder"
|
||||
|
@ -321,22 +291,30 @@ module Rails
|
|||
end
|
||||
|
||||
def javascript_gemfile_entry
|
||||
importmap_rails_entry =
|
||||
return [] if options[:skip_javascript]
|
||||
|
||||
case options[:javascript]
|
||||
when "importmap"
|
||||
GemfileEntry.version("importmap-rails", ">= 0.3.4", "Manage modern JavaScript using ESM without transpiling or bundling")
|
||||
when "webpack"
|
||||
GemfileEntry.version "webpacker", "~> 6.0.0.rc.5", "Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker"
|
||||
when "esbuild"
|
||||
GemfileEntry.version "esbuild-rails", "~> 0.1.2", "Transpile app-like JavaScript. Read more: https://github.com/rails/esbuild-rails"
|
||||
else
|
||||
raise "Unknown JavaScript approach: #{options[:javascript]}"
|
||||
end
|
||||
end
|
||||
|
||||
def hotwire_gemfile_entry
|
||||
return [] if options[:skip_javascript] || options[:skip_hotwire]
|
||||
|
||||
turbo_rails_entry =
|
||||
GemfileEntry.version("turbo-rails", ">= 0.7.4", "Hotwire's SPA-like page accelerator. Read more: https://turbo.hotwired.dev")
|
||||
GemfileEntry.version("turbo-rails", ">= 0.7.11", "Hotwire's SPA-like page accelerator. Read more: https://turbo.hotwired.dev")
|
||||
|
||||
stimulus_rails_entry =
|
||||
GemfileEntry.version("stimulus-rails", ">= 0.3.9", "Hotwire's modest JavaScript framework for the HTML you already have. Read more: https://stimulus.hotwired.dev")
|
||||
GemfileEntry.version("stimulus-rails", ">= 0.4.0", "Hotwire's modest JavaScript framework for the HTML you already have. Read more: https://stimulus.hotwired.dev")
|
||||
|
||||
if options[:skip_javascript]
|
||||
[]
|
||||
elsif options[:skip_hotwire]
|
||||
[ importmap_rails_entry ]
|
||||
else
|
||||
[ importmap_rails_entry, turbo_rails_entry, stimulus_rails_entry ]
|
||||
end
|
||||
[ turbo_rails_entry, stimulus_rails_entry ]
|
||||
end
|
||||
|
||||
def psych_gemfile_entry
|
||||
|
@ -385,18 +363,6 @@ module Rails
|
|||
!(options[:skip_bundle] || options[:pretend])
|
||||
end
|
||||
|
||||
def webpack_install?
|
||||
options[:webpack]
|
||||
end
|
||||
|
||||
def importmap_install?
|
||||
!(options[:skip_javascript] || options[:webpack])
|
||||
end
|
||||
|
||||
def hotwire_install?
|
||||
!(options[:skip_javascript] || options[:skip_hotwire])
|
||||
end
|
||||
|
||||
def depends_on_system_test?
|
||||
!(options[:skip_system_test] || options[:skip_test] || options[:api])
|
||||
end
|
||||
|
@ -409,44 +375,18 @@ module Rails
|
|||
bundle_command("install", "BUNDLE_IGNORE_MESSAGES" => "1") if bundle_install?
|
||||
end
|
||||
|
||||
def run_webpack
|
||||
return unless webpack_install?
|
||||
def run_javascript
|
||||
return if options[:skip_javascript] || !bundle_install?
|
||||
|
||||
unless bundle_install?
|
||||
say <<~EXPLAIN
|
||||
Skipping `rails webpacker:install` because `bundle install` was skipped.
|
||||
To complete setup, you must run `bundle install` followed by `rails webpacker:install`.
|
||||
EXPLAIN
|
||||
return
|
||||
case options[:javascript]
|
||||
when "importmap" then rails_command "importmap:install"
|
||||
when "webpack" then rails_command "webpacker:install"
|
||||
when "esbuild" then rails_command "esbuild:install"
|
||||
end
|
||||
|
||||
rails_command "webpacker:install"
|
||||
end
|
||||
|
||||
def run_importmap
|
||||
return unless importmap_install?
|
||||
|
||||
unless bundle_install?
|
||||
say <<~EXPLAIN
|
||||
Skipping `rails importmap:install` because `bundle install` was skipped.
|
||||
To complete setup, you must run `bundle install` followed by `rails importmap:install`.
|
||||
EXPLAIN
|
||||
return
|
||||
end
|
||||
|
||||
rails_command "importmap:install"
|
||||
end
|
||||
|
||||
def run_hotwire
|
||||
return unless hotwire_install?
|
||||
|
||||
unless bundle_install?
|
||||
say <<~EXPLAIN
|
||||
Skipping `rails turbo:install stimulus:install` because `bundle install` was skipped.
|
||||
To complete setup, you must run `bundle install` followed by `rails turbo:install stimulus:install`.
|
||||
EXPLAIN
|
||||
return
|
||||
end
|
||||
return if options[:skip_javascript] || options[:skip_hotwire] || !bundle_install?
|
||||
|
||||
rails_command "turbo:install stimulus:install"
|
||||
end
|
||||
|
|
|
@ -267,20 +267,11 @@ module Rails
|
|||
add_shared_options_for "application"
|
||||
|
||||
# Add rails command options
|
||||
class_option :version, type: :boolean, aliases: "-v", group: :rails,
|
||||
desc: "Show Rails version number and quit"
|
||||
|
||||
class_option :api, type: :boolean,
|
||||
desc: "Preconfigure smaller stack for API only apps"
|
||||
|
||||
class_option :minimal, type: :boolean,
|
||||
desc: "Preconfigure a minimal rails app"
|
||||
|
||||
class_option :skip_bundle, type: :boolean, aliases: "-B", default: false,
|
||||
desc: "Don't run bundle install"
|
||||
|
||||
class_option :webpack, type: :boolean, aliases: "--webpacker", default: false,
|
||||
desc: "Preconfigure Webpack"
|
||||
class_option :version, type: :boolean, aliases: "-v", group: :rails, desc: "Show Rails version number and quit"
|
||||
class_option :api, type: :boolean, desc: "Preconfigure smaller stack for API only apps"
|
||||
class_option :minimal, type: :boolean, desc: "Preconfigure a minimal rails app"
|
||||
class_option :javascript, type: :string, aliases: "-j", default: "importmap", desc: "Choose JavaScript approach"
|
||||
class_option :skip_bundle, type: :boolean, aliases: "-B", default: false, desc: "Don't run bundle install"
|
||||
|
||||
def initialize(*args)
|
||||
super
|
||||
|
@ -308,11 +299,7 @@ module Rails
|
|||
skip_javascript: true,
|
||||
skip_jbuilder: true,
|
||||
skip_system_test: true,
|
||||
skip_hotwire: true).tap do |option|
|
||||
if option[:webpack]
|
||||
option[:skip_javascript] = false
|
||||
end
|
||||
end.freeze
|
||||
skip_hotwire: true).freeze
|
||||
end
|
||||
|
||||
@after_bundle_callbacks = []
|
||||
|
@ -523,8 +510,7 @@ module Rails
|
|||
|
||||
public_task :apply_rails_template, :run_bundle
|
||||
public_task :generate_bundler_binstub
|
||||
public_task :run_webpack
|
||||
public_task :run_importmap
|
||||
public_task :run_javascript
|
||||
public_task :run_hotwire
|
||||
|
||||
def run_after_bundle_callbacks
|
||||
|
|
|
@ -11,11 +11,6 @@
|
|||
# policy.object_src :none
|
||||
# policy.script_src :self, :https
|
||||
# policy.style_src :self, :https
|
||||
<%- if options[:webpack] -%>
|
||||
# # If you are using webpack-dev-server then specify webpack-dev-server host
|
||||
# policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development?
|
||||
<%- end -%>
|
||||
|
||||
# # Specify URI for violation reports
|
||||
# # policy.report_uri "/csp-violation-report-endpoint"
|
||||
# end
|
||||
|
|
|
@ -5,10 +5,5 @@
|
|||
db/schema.rb linguist-generated
|
||||
<% end -%>
|
||||
|
||||
<% if options[:webpack] -%>
|
||||
# Mark the yarn lockfile as having been generated.
|
||||
yarn.lock linguist-generated
|
||||
<% end -%>
|
||||
|
||||
# Mark any vendored files as having been vendored.
|
||||
vendor/* linguist-vendored
|
||||
|
|
|
@ -9,10 +9,6 @@
|
|||
/<%= dummy_path %>/db/*.sqlite3-*
|
||||
<% end -%>
|
||||
/<%= dummy_path %>/log/*.log
|
||||
<% if options[:webpack] -%>
|
||||
/<%= dummy_path %>/node_modules/
|
||||
/<%= dummy_path %>/yarn-error.log
|
||||
<% end -%>
|
||||
<% unless skip_active_storage? -%>
|
||||
/<%= dummy_path %>/storage/
|
||||
<% end -%>
|
||||
|
|
|
@ -3,86 +3,71 @@
|
|||
require "generators/generators_test_helper"
|
||||
require "generators/action_text/install/install_generator"
|
||||
|
||||
module Webpacker
|
||||
extend self
|
||||
|
||||
def config
|
||||
Class.new do
|
||||
def source_path
|
||||
"app/packs"
|
||||
end
|
||||
|
||||
def source_entry_path
|
||||
"app/packs/entrypoints"
|
||||
end
|
||||
end.new
|
||||
end
|
||||
end
|
||||
|
||||
class ActionText::Generators::InstallGeneratorTest < Rails::Generators::TestCase
|
||||
include GeneratorsTestHelper
|
||||
|
||||
setup do
|
||||
Rails.application = Rails.application.class
|
||||
Rails.application.config.root = Pathname(destination_root)
|
||||
run_under_webpacker
|
||||
|
||||
FileUtils.mkdir_p("#{destination_root}/app/javascript")
|
||||
FileUtils.touch("#{destination_root}/app/javascript/application.js")
|
||||
|
||||
FileUtils.mkdir_p("#{destination_root}/config")
|
||||
FileUtils.touch("#{destination_root}/config/importmap.rb")
|
||||
end
|
||||
|
||||
teardown do
|
||||
Rails.application = Rails.application.instance
|
||||
run_under_asset_pipeline
|
||||
end
|
||||
Rails.application = Rails.application.instance
|
||||
end
|
||||
|
||||
test "installs JavaScript dependencies" do
|
||||
run_generator_instance
|
||||
yarn_commands = @yarn_commands.join("\n")
|
||||
FileUtils.touch("#{destination_root}/package.json")
|
||||
|
||||
assert_match %r"^add .*@rails/actiontext@", yarn_commands
|
||||
assert_match %r"^add .*trix@", yarn_commands
|
||||
run_generator_instance
|
||||
assert_match %r"yarn add @rails/actiontext trix", @run_commands.join("\n")
|
||||
end
|
||||
|
||||
test "throws warning for incomplete webpacker configuration" do
|
||||
output = run_generator_instance
|
||||
expected = "WARNING: Action Text can't locate your JavaScript bundle to add its package dependencies."
|
||||
|
||||
assert_match expected, output
|
||||
test "throws warning for missing entry point" do
|
||||
FileUtils.rm("#{destination_root}/app/javascript/application.js")
|
||||
assert_match "You must import the @rails/actiontext and trix JavaScript modules", run_generator_instance
|
||||
end
|
||||
|
||||
test "loads JavaScript dependencies in application.js" do
|
||||
application_js = Pathname("app/javascript/application.js").expand_path(destination_root)
|
||||
application_js.dirname.mkpath
|
||||
application_js.write("\n")
|
||||
|
||||
run_under_asset_pipeline
|
||||
test "imports JavaScript dependencies in application.js" do
|
||||
run_generator_instance
|
||||
|
||||
assert_file application_js do |content|
|
||||
assert_file "app/javascript/application.js" do |content|
|
||||
assert_match %r"^#{Regexp.escape 'import "@rails/actiontext"'}", content
|
||||
assert_match %r"^#{Regexp.escape 'import "trix"'}", content
|
||||
end
|
||||
end
|
||||
|
||||
test "creates Action Text stylesheet" do
|
||||
test "pins JavaScript dependencies in importmap.rb" do
|
||||
run_generator_instance
|
||||
|
||||
assert_file "config/importmap.rb" do |content|
|
||||
assert_match %r|pin "@rails/actiontext"|, content
|
||||
assert_match %r|pin "trix"|, content
|
||||
end
|
||||
end
|
||||
|
||||
test "creates Action Text stylesheet" do
|
||||
run_generator_instance
|
||||
assert_file "app/assets/stylesheets/actiontext.css"
|
||||
end
|
||||
|
||||
test "creates Active Storage view partial" do
|
||||
run_generator_instance
|
||||
|
||||
assert_file "app/views/active_storage/blobs/_blob.html.erb"
|
||||
end
|
||||
|
||||
test "creates Action Text content view layout" do
|
||||
run_generator_instance
|
||||
|
||||
assert_file "app/views/layouts/action_text/contents/_content.html.erb"
|
||||
end
|
||||
|
||||
test "creates migrations" do
|
||||
run_generator_instance
|
||||
|
||||
assert_migration "db/migrate/create_active_storage_tables.active_storage.rb"
|
||||
assert_migration "db/migrate/create_action_text_tables.action_text.rb"
|
||||
end
|
||||
|
@ -99,36 +84,13 @@ class ActionText::Generators::InstallGeneratorTest < Rails::Generators::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
test "run just for asset pipeline" do
|
||||
run_under_asset_pipeline
|
||||
|
||||
application_js = Pathname("app/javascript/application.js").expand_path(destination_root)
|
||||
application_js.dirname.mkpath
|
||||
application_js.write ""
|
||||
|
||||
run_generator_instance
|
||||
|
||||
assert_file application_js do |content|
|
||||
assert_match %r"trix", content
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def run_generator_instance
|
||||
@yarn_commands = []
|
||||
yarn_command_stub = -> (command, *) { @yarn_commands << command }
|
||||
@run_commands = []
|
||||
run_command_stub = -> (command, *) { @run_commands << command }
|
||||
|
||||
generator.stub :yarn_command, yarn_command_stub do
|
||||
generator.stub :run, run_command_stub do
|
||||
with_database_configuration { super }
|
||||
end
|
||||
end
|
||||
|
||||
def run_under_webpacker
|
||||
# Stub Webpacker engine presence to exercise path
|
||||
Kernel.silence_warnings { Webpacker.const_set(:Engine, true) } rescue nil
|
||||
end
|
||||
|
||||
def run_under_asset_pipeline
|
||||
Kernel.silence_warnings { Webpacker.send(:remove_const, :Engine) } rescue nil
|
||||
end
|
||||
end
|
||||
|
|
|
@ -208,15 +208,6 @@ class AppGeneratorTest < Rails::Generators::TestCase
|
|||
assert_file "#{app_root}/config/application.rb", /\s+config\.load_defaults #{Rails::VERSION::STRING.to_f}/
|
||||
end
|
||||
|
||||
def test_csp_initializer_include_connect_src_example
|
||||
app_root = File.join(destination_root, "myapp")
|
||||
run_generator [app_root, "--webpack"]
|
||||
|
||||
assert_file "#{app_root}/config/initializers/content_security_policy.rb" do |content|
|
||||
assert_match(/# policy\.connect_src/, content)
|
||||
end
|
||||
end
|
||||
|
||||
def test_app_update_keep_the_cookie_serializer_if_it_is_already_configured
|
||||
app_root = File.join(destination_root, "myapp")
|
||||
run_generator [app_root]
|
||||
|
@ -774,8 +765,8 @@ class AppGeneratorTest < Rails::Generators::TestCase
|
|||
generator([destination_root], skip_javascript: true)
|
||||
|
||||
command_check = -> command, *_ do
|
||||
if command == "webpacker:install"
|
||||
flunk "`webpacker:install` expected to not be called."
|
||||
if command == "importmap:install"
|
||||
flunk "`importmap:install` expected to not be called."
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -783,7 +774,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
|
|||
run_generator_instance
|
||||
end
|
||||
|
||||
assert_no_gem "webpacker"
|
||||
assert_no_gem "importmap-rails"
|
||||
|
||||
assert_file "config/initializers/content_security_policy.rb" do |content|
|
||||
assert_no_match(/policy\.connect_src/, content)
|
||||
|
@ -795,7 +786,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
|
|||
end
|
||||
|
||||
def test_webpack_option
|
||||
generator([destination_root], webpack: true)
|
||||
generator([destination_root], javascript: "webpack")
|
||||
|
||||
webpacker_called = 0
|
||||
command_check = -> command, *_ do
|
||||
|
@ -822,7 +813,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
|
|||
end
|
||||
assert_file "app/javascript/application.js" do |content|
|
||||
assert_match(/turbo/, content)
|
||||
assert_match(/stimulus/, content)
|
||||
assert_match(/controllers/, content)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -2,17 +2,18 @@
|
|||
|
||||
require "generators/generators_test_helper"
|
||||
require "rails/generators/channel/channel_generator"
|
||||
require "byebug"
|
||||
|
||||
class ChannelGeneratorTest < Rails::Generators::TestCase
|
||||
include GeneratorsTestHelper
|
||||
tests Rails::Generators::ChannelGenerator
|
||||
|
||||
setup do
|
||||
FileUtils.mkdir_p("#{destination_root}/app/javascript")
|
||||
FileUtils.touch("#{destination_root}/app/javascript/application.js")
|
||||
use_with_javascript
|
||||
use_under_importmap
|
||||
end
|
||||
|
||||
def test_application_cable_skeleton_is_created
|
||||
test "shared channel files are created" do
|
||||
run_generator ["books"]
|
||||
|
||||
assert_file "app/channels/application_cable/channel.rb" do |cable|
|
||||
|
@ -24,7 +25,7 @@ class ChannelGeneratorTest < Rails::Generators::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_channel_is_created
|
||||
test "specific channel files are created under importmap" do
|
||||
run_generator ["chat"]
|
||||
|
||||
assert_file "app/channels/chat_channel.rb" do |channel|
|
||||
|
@ -36,7 +37,18 @@ class ChannelGeneratorTest < Rails::Generators::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_channel_with_multiple_actions_is_created
|
||||
test "specific channel files are created under node" do
|
||||
use_under_node
|
||||
generator(["chat"]).stub(:install_javascript_dependencies, true) do
|
||||
run_generator_instance
|
||||
|
||||
assert_file "app/javascript/channels/chat_channel.js" do |channel|
|
||||
assert_match(/import consumer from ".\/consumer"\s+consumer\.subscriptions\.create\("ChatChannel/, channel)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
test "channel with multiple actions is created" do
|
||||
run_generator ["chat", "speak", "mute"]
|
||||
|
||||
assert_file "app/channels/chat_channel.rb" do |channel|
|
||||
|
@ -52,7 +64,51 @@ class ChannelGeneratorTest < Rails::Generators::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_channel_asset_is_not_created_when_skip_assets_is_passed
|
||||
test "shared channel javascript files are created" do
|
||||
run_generator ["books"]
|
||||
|
||||
assert_file "app/javascript/channels/index.js"
|
||||
assert_file "app/javascript/channels/consumer.js"
|
||||
end
|
||||
|
||||
test "import channels in javascript entrypoint" do
|
||||
run_generator ["books"]
|
||||
|
||||
assert_file "app/javascript/application.js" do |entrypoint|
|
||||
assert_match %r|import "channels"|, entrypoint
|
||||
end
|
||||
end
|
||||
|
||||
test "import channels in javascript entrypoint under node" do
|
||||
use_under_node
|
||||
generator(["chat"]).stub(:install_javascript_dependencies, true) do
|
||||
run_generator_instance
|
||||
|
||||
assert_file "app/javascript/application.js" do |entrypoint|
|
||||
assert_match %r|import "./channels"|, entrypoint
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
test "pin javascript dependencies" do
|
||||
run_generator ["chat"]
|
||||
|
||||
assert_file "config/importmap.rb" do |content|
|
||||
assert_match %r|pin "@rails/actioncable"|, content
|
||||
assert_match %r|pin_all_from "app/javascript/channels"|, content
|
||||
end
|
||||
end
|
||||
|
||||
test "first setup only happens once" do
|
||||
run_generator ["chat"]
|
||||
assert_file "app/javascript/channels/consumer.js"
|
||||
|
||||
FileUtils.rm("#{destination_root}/app/javascript/channels/consumer.js")
|
||||
run_generator ["another"]
|
||||
assert_no_file "app/javascript/channels/consumer.js"
|
||||
end
|
||||
|
||||
test "javascripts not generated when assets are skipped" do
|
||||
run_generator ["chat", "--skip-assets"]
|
||||
|
||||
assert_file "app/channels/chat_channel.rb" do |channel|
|
||||
|
@ -62,15 +118,7 @@ class ChannelGeneratorTest < Rails::Generators::TestCase
|
|||
assert_no_file "app/javascript/channels/chat_channel.js"
|
||||
end
|
||||
|
||||
def test_consumer_js_is_created_if_not_present_already
|
||||
run_generator ["chat"]
|
||||
FileUtils.rm("#{destination_root}/app/javascript/channels/consumer.js")
|
||||
run_generator ["camp"]
|
||||
|
||||
assert_file "app/javascript/channels/consumer.js"
|
||||
end
|
||||
|
||||
def test_invokes_default_test_framework
|
||||
test "invokes default test framework" do
|
||||
run_generator %w(chat -t=test_unit)
|
||||
|
||||
assert_file "test/channels/chat_channel_test.rb" do |test|
|
||||
|
@ -80,7 +128,7 @@ class ChannelGeneratorTest < Rails::Generators::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_channel_on_revoke
|
||||
test "revoking" do
|
||||
run_generator ["chat"]
|
||||
run_generator ["chat"], behavior: :revoke
|
||||
|
||||
|
@ -93,7 +141,7 @@ class ChannelGeneratorTest < Rails::Generators::TestCase
|
|||
assert_file "app/javascript/channels/consumer.js"
|
||||
end
|
||||
|
||||
def test_channel_suffix_is_not_duplicated
|
||||
test "channel suffix is not duplicated" do
|
||||
run_generator ["chat_channel"]
|
||||
|
||||
assert_no_file "app/channels/chat_channel_channel.rb"
|
||||
|
@ -105,4 +153,21 @@ class ChannelGeneratorTest < Rails::Generators::TestCase
|
|||
assert_no_file "test/channels/chat_channel_channel_test.rb"
|
||||
assert_file "test/channels/chat_channel_test.rb"
|
||||
end
|
||||
|
||||
private
|
||||
def use_with_javascript
|
||||
FileUtils.mkdir_p("#{destination_root}/app/javascript")
|
||||
FileUtils.touch("#{destination_root}/app/javascript/application.js")
|
||||
end
|
||||
|
||||
def use_under_importmap
|
||||
FileUtils.mkdir_p("#{destination_root}/config")
|
||||
FileUtils.touch("#{destination_root}/config/importmap.rb")
|
||||
FileUtils.rm_rf("#{destination_root}/package.json")
|
||||
end
|
||||
|
||||
def use_under_node
|
||||
FileUtils.touch("#{destination_root}/package.json")
|
||||
FileUtils.rm_rf("#{destination_root}/config/importmap.rb")
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue