Skip to content

Commit

Permalink
Resolve errors blocking usage in a mountable Rails engine (#1694)
Browse files Browse the repository at this point in the history
* Run binstubs based on Rails.root

This changes the assumption that these tasks are being run in the rails
root to explicitly determine the binstub directory based on the Rails
root.

This fixes the issue for when the tasks are being run from a mounted
dummy app as part of a Rails engine.

Given a fresh Rails plugin: `rails plugin new rails-plugin --mountable`
that has webpacker gem installed.

Before this change running running `app:webpacker:check_binstubs` would
check in `./bin` for webpack binstubs whereas they are actually expected
in `test/dummy/bin`.

After this change the binstubs for the rails app in `test/dummy/bin` are
checked as expected.

* Allow dependant tasks to run under a prefix

If you have a scenario where a Rails apps tasks are placed under a
namespace these tasks that invoke other tasks don't work.

The common scenario where this happens is when a Rails engine is mounted
in a dummy app as per: https://github.com/rails/rails/blob/master/railties/lib/rails/tasks/engine.rake#L4-L17

This change resolves the issues where you receive an error of task not
found when running an app command e.g. app:assets:precompile.

If we are able to know a task dependency prior to runtime we don't have
to do anything complex as per the change for `clobber` since it being
defined outside a block does not require a namespace.

However for the tasks that we need to perform actions at runtime we have
to work out the prefix of the task that was executed to then re-use this
on the tasks we want to execute.

* Explicitly require Rails::Engine

When this gem is running inside a Rails engine to do a task such as
`app:assets:precompile` an error is thrown:

```
➜  rails-plugin git:(master) ✗ rake app:assets:precompile
Compiling…
Compilation failed:
/Users/kevindew/dev/webpacker/lib/webpacker/railtie.rb:6:in `<top (required)>': uninitialized constant Rails::Engine (NameError)
	from /Users/kevindew/dev/webpacker/lib/webpacker.rb:38:in `require'
	from /Users/kevindew/dev/webpacker/lib/webpacker.rb:38:in `<top (required)>'
	from /Users/kevindew/dev/rails-plugin/test/dummy/bin/webpack:13:in `require'
	from /Users/kevindew/dev/rails-plugin/test/dummy/bin/webpack:13:in `<main>'
```
Explicitly requiring rails/engine resolves this.

* Run yarn tasks within the Rails root directory

This resolves problems that occur when the expectation is that the
Rails.root is the same as the working directory of the rakefile.

A common situation for this is when you have a mounted Rails engine in
the test/dummy app to run your engine.

This changes the yarn commands to run in the directory where you would
have the package.json for the rails app that would be used for mounting
rather than the root of the engine. And the node_modules are also
installed within that directory so they are in the expected place for
tasks such as app:assets:precompile.

* Update the gitignore within the rails root

The contents of the gitignore only really make sense for the root
directory of a Rails application. If we are dealing with a Rails engine
that mounts the engine onto a test one for usage then it does not make
sense to update the root .gitignore and if someone does have one in the
`test/dummy` directory then that one should be updated instead.

* Run webpacker:info in Rails.root

This is resolution to problems that occur with a mounted Rails engine.
With one of these you would want to run `rake app:webpacker:info` and
have it run against the Rails app you'd be mounting in (normally in
test/dummy) however it would incorrectly run in the root directory -
where you probably don't want to have webpacker and webpacker-dev-server
installed.

* Show a relative path when describing configuration missing

For a mounted Rails engine we need the config to be in the application
mount directory e.g. test/dummy/config/webpacker.yml

By running this based on pwd we get this in a Rails engine app:

```
➜  rails-plugin rake app:webpacker:verify_install
RAILS_ENV=development environment is not defined in config/webpacker.yml, falling back to production environment
Configuration test/dummy/config/webpacker.yml file not found.
Make sure webpacker:install is run successfully before running dependent tasks
```

Whereas in a normal Rails installation it outputs unchanged:

```
➜  full-app git:(master) ✗ rake webpacker:verify_install
RAILS_ENV=development environment is not defined in config/webpacker.yml, falling back to production environment
Configuration config/webpacker.yml file not found.
Make sure webpacker:install is run successfully before running dependent tasks
```

* Basic tests for a mounted application

This sets up a barebones mounted Rails engine so that tests can be
performed against it.

This follows a similar approach to the other rake tasks test and thus
doesn't intend to be too exhaustive in what is testing and is mostly a
cursory test that one of the tasks does perform differently in a mounted
Rails engine context.

Co-authored-by: Gaurav Tiwari <[email protected]>
  • Loading branch information
kevindew and gauravtiwari authored Aug 31, 2020
1 parent ac93e12 commit 741b314
Show file tree
Hide file tree
Showing 28 changed files with 242 additions and 57 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/.bundle
/pkg
/test/mounted_app/test/dummy/log
/test/test_app/log
node_modules
.byebug_history
Expand Down
6 changes: 4 additions & 2 deletions lib/install/angular.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
say "Copying hello_angular app to #{Webpacker.config.source_path}"
directory "#{__dir__}/examples/angular/hello_angular", "#{Webpacker.config.source_path}/hello_angular"

say "Installing all angular dependencies"
run "yarn add core-js zone.js rxjs @angular/core @angular/common @angular/compiler @angular/platform-browser @angular/platform-browser-dynamic"
Dir.chdir(Rails.root) do
say "Installing all angular dependencies"
run "yarn add core-js zone.js rxjs @angular/core @angular/common @angular/compiler @angular/platform-browser @angular/platform-browser-dynamic"
end

if Rails::VERSION::MAJOR == 5 && Rails::VERSION::MINOR > 1
say "You need to enable unsafe-eval rule.", :yellow
Expand Down
6 changes: 4 additions & 2 deletions lib/install/coffee.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
copy_file "#{__dir__}/examples/coffee/hello_coffee.coffee",
"#{Webpacker.config.source_entry_path}/hello_coffee.coffee"

say "Installing all Coffeescript dependencies"
run "yarn add [email protected] coffee-loader"
Dir.chdir(Rails.root) do
say "Installing all Coffeescript dependencies"
run "yarn add [email protected] coffee-loader"
end

say "Webpacker now supports Coffeescript 🎉", :green
12 changes: 7 additions & 5 deletions lib/install/elm.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@
copy_file "#{__dir__}/examples/elm/Main.elm",
"#{Webpacker.config.source_path}/Main.elm"

say "Installing all Elm dependencies"
run "yarn add elm elm-webpack-loader"
run "yarn add --dev elm-hot-webpack-loader"
run "yarn run elm init"
run "yarn run elm make #{Webpacker.config.source_path}/Main.elm"
Dir.chdir(Rails.root) do
say "Installing all Elm dependencies"
run "yarn add elm elm-webpack-loader"
run "yarn add --dev elm-hot-webpack-loader"
run "yarn run elm init"
run "yarn run elm make #{Webpacker.config.source_path}/Main.elm"
end

say "Updating webpack paths to include .elm file extension"
insert_into_file Webpacker.config.config_path, "- .elm\n".indent(4), after: /\s+extensions:\n/
Expand Down
6 changes: 4 additions & 2 deletions lib/install/erb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
copy_file "#{__dir__}/examples/erb/hello_erb.js.erb",
"#{Webpacker.config.source_entry_path}/hello_erb.js.erb"

say "Installing all Erb dependencies"
run "yarn add rails-erb-loader"
Dir.chdir(Rails.root) do
say "Installing all Erb dependencies"
run "yarn add rails-erb-loader"
end

say "Webpacker now supports Erb in JS 🎉", :green
6 changes: 4 additions & 2 deletions lib/install/react.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
say "Updating webpack paths to include .jsx file extension"
insert_into_file Webpacker.config.config_path, "- .jsx\n".indent(4), after: /\s+extensions:\n/

say "Installing all react dependencies"
run "yarn add react react-dom @babel/preset-react prop-types babel-plugin-transform-react-remove-prop-types"
Dir.chdir(Rails.root) do
say "Installing all react dependencies"
run "yarn add react react-dom @babel/preset-react prop-types babel-plugin-transform-react-remove-prop-types"
end

say "Webpacker now supports react.js 🎉", :green
6 changes: 4 additions & 2 deletions lib/install/stimulus.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
say "Creating controllers directory"
directory "#{__dir__}/examples/stimulus/controllers", "#{Webpacker.config.source_path}/controllers"

say "Installing all Stimulus dependencies"
run "yarn add stimulus"
Dir.chdir(Rails.root) do
say "Installing all Stimulus dependencies"
run "yarn add stimulus"
end

say "Webpacker now supports Stimulus.js 🎉", :green
25 changes: 14 additions & 11 deletions lib/install/template.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@

apply "#{__dir__}/binstubs.rb"

if File.exists?(".gitignore")
append_to_file ".gitignore" do
git_ignore_path = Rails.root.join(".gitignore")
if File.exists?(git_ignore_path)
append_to_file git_ignore_path do
"\n" +
"/public/packs\n" +
"/public/packs-test\n" +
Expand All @@ -28,16 +29,18 @@
end
end

if Webpacker::VERSION =~ /^[0-9]+\.[0-9]+\.[0-9]+$/
say "Installing all JavaScript dependencies [#{Webpacker::VERSION}]"
run "yarn add @rails/webpacker@#{Webpacker::VERSION}"
else
say "Installing all JavaScript dependencies [from prerelease rails/webpacker]"
run "yarn add @rails/webpacker@next"
end
Dir.chdir(Rails.root) do
if Webpacker::VERSION =~ /^[0-9]+\.[0-9]+\.[0-9]+$/
say "Installing all JavaScript dependencies [#{Webpacker::VERSION}]"
run "yarn add @rails/webpacker@#{Webpacker::VERSION}"
else
say "Installing all JavaScript dependencies [from prerelease rails/webpacker]"
run "yarn add @rails/webpacker@next"
end

say "Installing dev server for live reloading"
run "yarn add --dev webpack-dev-server"
say "Installing dev server for live reloading"
run "yarn add --dev webpack-dev-server"
end

insert_into_file Rails.root.join("package.json").to_s, before: /\n}\n*$/ do
<<~JSON.chomp
Expand Down
6 changes: 4 additions & 2 deletions lib/install/typescript.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
copy_file "#{__dir__}/examples/typescript/hello_typescript.ts",
"#{Webpacker.config.source_entry_path}/hello_typescript.ts"

say "Installing all typescript dependencies"
run "yarn add typescript @babel/preset-typescript #{additional_packages}"
Dir.chdir(Rails.root) do
say "Installing all typescript dependencies"
run "yarn add typescript @babel/preset-typescript #{additional_packages}"
end

say "Webpacker now supports typescript 🎉", :green
6 changes: 4 additions & 2 deletions lib/install/vue.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@
copy_file "#{__dir__}/examples/vue/app.vue",
"#{Webpacker.config.source_path}/app.vue"

say "Installing all Vue dependencies"
run "yarn add vue vue-loader vue-template-compiler"
Dir.chdir(Rails.root) do
say "Installing all Vue dependencies"
run "yarn add vue vue-loader vue-template-compiler"
end

if Rails::VERSION::MAJOR == 5 && Rails::VERSION::MINOR > 1
say "You need to enable unsafe-eval rule.", :yellow
Expand Down
10 changes: 6 additions & 4 deletions lib/tasks/installers.rake
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,21 @@ dependencies = {
"Angular": [:typescript]
}

bin_path = ENV["BUNDLE_BIN"] || "./bin"
bin_path = ENV["BUNDLE_BIN"] || Rails.root.join("bin")

namespace :webpacker do
namespace :install do
installers.each do |name, task_name|
desc "Install everything needed for #{name}"
task task_name => ["webpacker:verify_install"] do
task task_name => ["webpacker:verify_install"] do |task|
prefix = task.name.split(/#|webpacker:install/).first

template = File.expand_path("../install/#{task_name}.rb", __dir__)
base_path =
if Rails::VERSION::MAJOR >= 5
"#{RbConfig.ruby} #{bin_path}/rails app:template"
"#{RbConfig.ruby} #{bin_path}/rails #{prefix}app:template"
else
"#{RbConfig.ruby} #{bin_path}/rake rails:template"
"#{RbConfig.ruby} #{bin_path}/rake #{prefix}rails:template"
end

dependencies[name] ||= []
Expand Down
10 changes: 6 additions & 4 deletions lib/tasks/webpacker/binstubs.rake
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
binstubs_template_path = File.expand_path("../../install/binstubs.rb", __dir__).freeze
bin_path = ENV["BUNDLE_BIN"] || "./bin"
bin_path = ENV["BUNDLE_BIN"] || Rails.root.join("bin")

namespace :webpacker do
desc "Installs Webpacker binstubs in this application"
task binstubs: [:check_node, :check_yarn] do
task binstubs: [:check_node, :check_yarn] do |task|
prefix = task.name.split(/#|webpacker:binstubs/).first

if Rails::VERSION::MAJOR >= 5
exec "#{RbConfig.ruby} #{bin_path}/rails app:template LOCATION=#{binstubs_template_path}"
exec "#{RbConfig.ruby} #{bin_path}/rails #{prefix}app:template LOCATION=#{binstubs_template_path}"
else
exec "#{RbConfig.ruby} #{bin_path}/rake rails:template LOCATION=#{binstubs_template_path}"
exec "#{RbConfig.ruby} #{bin_path}/rake #{prefix}rails:template LOCATION=#{binstubs_template_path}"
end
end
end
2 changes: 1 addition & 1 deletion lib/tasks/webpacker/check_binstubs.rake
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace :webpacker do
desc "Verifies that webpack & webpack-dev-server are present."
task :check_binstubs do
unless File.exist?("bin/webpack")
unless File.exist?(Rails.root.join("bin/webpack"))
$stderr.puts "webpack binstubs not found.\n"\
"Have you run rails webpacker:install ?\n"\
"Make sure the bin directory or binstubs are not included in .gitignore\n"\
Expand Down
6 changes: 4 additions & 2 deletions lib/tasks/webpacker/compile.rake
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ end
def enhance_assets_precompile
# yarn:install was added in Rails 5.1
deps = yarn_install_available? ? [] : ["webpacker:yarn_install"]
Rake::Task["assets:precompile"].enhance(deps) do
Rake::Task["webpacker:compile"].invoke
Rake::Task["assets:precompile"].enhance(deps) do |task|
prefix = task.name.split(/#|assets:precompile/).first

Rake::Task["#{prefix}webpacker:compile"].invoke
end
end

Expand Down
22 changes: 12 additions & 10 deletions lib/tasks/webpacker/info.rake
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@ require "webpacker/version"
namespace :webpacker do
desc "Provide information on Webpacker's environment"
task :info do
$stdout.puts "Ruby: #{`ruby --version`}"
$stdout.puts "Rails: #{Rails.version}"
$stdout.puts "Webpacker: #{Webpacker::VERSION}"
$stdout.puts "Node: #{`node --version`}"
$stdout.puts "Yarn: #{`yarn --version`}"
Dir.chdir(Rails.root) do
$stdout.puts "Ruby: #{`ruby --version`}"
$stdout.puts "Rails: #{Rails.version}"
$stdout.puts "Webpacker: #{Webpacker::VERSION}"
$stdout.puts "Node: #{`node --version`}"
$stdout.puts "Yarn: #{`yarn --version`}"

$stdout.puts "\n"
$stdout.puts "@rails/webpacker: \n#{`npm list @rails/webpacker version`}"
$stdout.puts "\n"
$stdout.puts "@rails/webpacker: \n#{`npm list @rails/webpacker version`}"

$stdout.puts "Is bin/webpack present?: #{File.exist? 'bin/webpack'}"
$stdout.puts "Is bin/webpack-dev-server present?: #{File.exist? 'bin/webpack-dev-server'}"
$stdout.puts "Is bin/yarn present?: #{File.exist? 'bin/yarn'}"
$stdout.puts "Is bin/webpack present?: #{File.exist? 'bin/webpack'}"
$stdout.puts "Is bin/webpack-dev-server present?: #{File.exist? 'bin/webpack-dev-server'}"
$stdout.puts "Is bin/yarn present?: #{File.exist? 'bin/yarn'}"
end
end
end
10 changes: 6 additions & 4 deletions lib/tasks/webpacker/install.rake
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
install_template_path = File.expand_path("../../install/template.rb", __dir__).freeze
bin_path = ENV["BUNDLE_BIN"] || "./bin"
bin_path = ENV["BUNDLE_BIN"] || Rails.root.join("bin")

namespace :webpacker do
desc "Install Webpacker in this application"
task install: [:check_node, :check_yarn] do
task install: [:check_node, :check_yarn] do |task|
prefix = task.name.split(/#|webpacker:install/).first

if Rails::VERSION::MAJOR >= 5
exec "#{RbConfig.ruby} #{bin_path}/rails app:template LOCATION=#{install_template_path}"
exec "#{RbConfig.ruby} #{bin_path}/rails #{prefix}app:template LOCATION=#{install_template_path}"
else
exec "#{RbConfig.ruby} #{bin_path}/rake rails:template LOCATION=#{install_template_path}"
exec "#{RbConfig.ruby} #{bin_path}/rake #{prefix}rails:template LOCATION=#{install_template_path}"
end
end
end
3 changes: 2 additions & 1 deletion lib/tasks/webpacker/verify_install.rake
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ namespace :webpacker do
desc "Verifies if Webpacker is installed"
task verify_install: [:check_node, :check_yarn, :check_binstubs] do
unless Webpacker.config.config_path.exist?
$stderr.puts "Configuration config/webpacker.yml file not found. \n"\
path = Webpacker.config.config_path.relative_path_from(Pathname.new(pwd)).to_s
$stderr.puts "Configuration #{path} file not found. \n"\
"Make sure webpacker:install is run successfully before " \
"running dependent tasks"
exit!
Expand Down
4 changes: 3 additions & 1 deletion lib/tasks/webpacker/yarn_install.rake
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ namespace :webpacker do
node_env = ENV.fetch("NODE_ENV") do
valid_node_envs.include?(Rails.env) ? Rails.env : "production"
end
system({ "NODE_ENV" => node_env }, "yarn install --no-progress --frozen-lockfile")
Dir.chdir(Rails.root) do
system({ "NODE_ENV" => node_env }, "yarn install --no-progress --frozen-lockfile")
end
end
end
39 changes: 39 additions & 0 deletions test/engine_rake_tasks_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
require "test_helper"

class EngineRakeTasksTest < Minitest::Test
def setup
remove_webpack_binstubs
end

def teardown
remove_webpack_binstubs
end

def test_task_mounted
output = Dir.chdir(mounted_app_path) { `rake -T` }
assert_includes output, "app:webpacker"
end

def test_binstubs
Dir.chdir(mounted_app_path) { `bundle exec rake app:webpacker:binstubs` }
webpack_binstub_paths.each { |path| assert File.exist?(path) }
end

private
def mounted_app_path
File.expand_path("mounted_app", __dir__)
end

def webpack_binstub_paths
[
"#{mounted_app_path}/test/dummy/bin/webpack",
"#{mounted_app_path}/test/dummy/bin/webpack-dev-server",
]
end

def remove_webpack_binstubs
webpack_binstub_paths.each do |path|
File.delete(path) if File.exist?(path)
end
end
end
4 changes: 4 additions & 0 deletions test/mounted_app/Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
require "bundler/setup"

APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
load "rails/tasks/engine.rake"
3 changes: 3 additions & 0 deletions test/mounted_app/test/dummy/Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
require_relative "config/application"

Rails.application.load_tasks
3 changes: 3 additions & 0 deletions test/mounted_app/test/dummy/bin/rails
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env ruby
APP_PATH = File.expand_path("../config/application", __dir__)
require "rails/commands"
3 changes: 3 additions & 0 deletions test/mounted_app/test/dummy/bin/rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env ruby
require "rake"
Rake.application.run
5 changes: 5 additions & 0 deletions test/mounted_app/test/dummy/config.ru
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# This file allows the `Rails.root` to be correctly determined.

require_relative "config/environment"

run Rails.application
10 changes: 10 additions & 0 deletions test/mounted_app/test/dummy/config/application.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
require "action_controller/railtie"
require "action_view/railtie"
require "webpacker"

module TestDummyApp
class Application < Rails::Application
config.secret_key_base = "abcdef"
config.eager_load = true
end
end
3 changes: 3 additions & 0 deletions test/mounted_app/test/dummy/config/environment.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
require_relative "application"

Rails.application.initialize!
Loading

0 comments on commit 741b314

Please sign in to comment.