Skip to content

Support server-rendering from webpack-dev-server #687

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Apr 10, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ rvm:
- jruby-9.0.1.0

gemfile:
# These have webpacker:
- gemfiles/rails_4.2_sprockets_4.gemfile
- gemfiles/rails_5_no_sprockets_webpacker.gemfile
# These don't have webpacker:
- gemfiles/rails_3.2.gemfile
- gemfiles/rails_4.0.5.gemfile
- gemfiles/rails_4.0_with_therubyracer.gemfile
- gemfiles/rails_4.1.gemfile
- gemfiles/rails_4.2_sprockets_2.gemfile
- gemfiles/rails_4.2_sprockets_3.gemfile
- gemfiles/rails_4.2_sprockets_4.gemfile
- gemfiles/rails_5_no_sprockets_webpacker.gemfile
- gemfiles/rails_5_no_sprockets.gemfile
- gemfiles/rails_5_sprockets_4.gemfile

Expand Down
6 changes: 5 additions & 1 deletion lib/react/server_rendering/webpacker_manifest_container.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@

module React
module ServerRendering
CLIENT_REQUIRE = %r{__webpack_require__\(.*webpack-dev-server\/client\/index\.js.*\n}

# Get a compiled file from Webpacker
class WebpackerManifestContainer
def find_asset(logical_path)
asset_path = Webpacker::Manifest.lookup(logical_path) # raises if not found
if asset_path.start_with?("http")
# TODO: this includes webpack-dev-server code which causes ExecJS to 💥
# this includes `webpack-dev-server/client/index.js` code which causes ExecJS to 💥
dev_server_asset = open(asset_path).read
dev_server_asset.sub!(CLIENT_REQUIRE, '//\0')
dev_server_asset
else
full_path = File.join(
# TODO: using `.parent` here won't work for nonstandard configurations
Expand Down
2 changes: 1 addition & 1 deletion test/dummy/app/javascript/components/GreetingMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ module.exports = React.createClass({
},
render: function() {
return React.DOM.div({},
React.DOM.div({}, this.state.greeting, ' ', this.props.name),
React.DOM.div({}, this.state.greeting, ' from Webpacker ', this.props.name ),
React.DOM.button({onClick: this.goodbye}, 'Goodbye')
);
}
Expand Down
11 changes: 11 additions & 0 deletions test/react/rails/pages_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,15 @@ class PagesControllerTest < ActionController::TestCase
assert_includes(response.body, "Hello")
end
end

WebpackerHelpers.when_webpacker_available do
test "it mounts components from the dev server" do
WebpackerHelpers.with_dev_server do
get :show, id: 1, prerender: true
assert_match /Hello<!--.*--> from Webpacker/, response.body
get :show, id: 1, prerender: true, greeting: "Howdy"
assert_match /Howdy<!--.*--> from Webpacker/, response.body
end
end
end
end
2 changes: 1 addition & 1 deletion test/react/rails/webpacker_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class ReactRailsWebpackerTest < ActionDispatch::IntegrationTest
WebpackerHelpers.clear_webpacker_packs
end

test 'it mounts pages from the pack' do
test 'it mounts components from the pack' do
visit '/pack_component'
assert page.has_content?('Export Default')
assert page.has_content?('Named Export')
Expand Down
55 changes: 7 additions & 48 deletions test/react/server_rendering/webpacker_manifest_container_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,55 +18,14 @@ class WebpackerManifestContainerTest < ActiveSupport::TestCase
end

def test_it_loads_from_webpack_dev_server
webpack_dev_server = fork do
Dir.chdir("test/dummy") do
exec "RAILS_ENV=development ./bin/webpack-dev-server "
end
WebpackerHelpers.with_dev_server do
container = React::ServerRendering::WebpackerManifestContainer.new
js_file = container.find_asset("application.js")
# Main file:
assert_includes js_file, "ReactRailsUJS.loadContext(ctx)"
# Bundled dependencies:
assert_includes js_file, "ExportDefaultComponent"
end

detected_dev_server = false
60.times do |i|
begin
# Make sure that the manifest has been updated:
Webpacker::Manifest.load("./test/dummy/public/packs/manifest.json")
webpack_manifest = Webpacker::Manifest.instance.data
example_asset_path = webpack_manifest.values.first
if example_asset_path.nil?
puts "Manifest is blank, all manifests:"
Dir.glob("./test/dummy/public/packs/*.json").each do |f|
puts f
puts File.read(f)
end
next
end
assert_includes example_asset_path, "http://localhost:8080"
# Make sure the dev server is up:
open("http://localhost:8080/application.js")
detected_dev_server = true
break
rescue StandardError => err
puts err.message
ensure
sleep 0.5
puts i
end
end

# If we didn't hook up with a dev server after 10s,
# fail loudly.
assert detected_dev_server

container = React::ServerRendering::WebpackerManifestContainer.new
js_file = container.find_asset("application.js")
# Main file:
assert_includes js_file, "ReactRailsUJS.loadContext(ctx)"
# Bundled dependencies:
assert_includes js_file, "ExportDefaultComponent"
ensure
Process.kill(9, webpack_dev_server)
Process.wait
# Remove the dev-server packs:
WebpackerHelpers.clear_webpacker_packs
end
end
end
77 changes: 77 additions & 0 deletions test/support/webpacker_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,81 @@ def compile_if_missing
def clear_webpacker_packs
FileUtils.rm_rf(PACKS_DIRECTORY)
end

# Start a webpack-dev-server
# Call the block
# Make sure to clean up the server
def with_dev_server
# Start the server in a forked process:
webpack_dev_server = Dir.chdir("test/dummy") do
spawn "RAILS_ENV=development ./bin/webpack-dev-server "
end

detected_dev_server = false

# Wait for it to start up, make sure it's there by connecting to it:
30.times do |i|
begin
# Make sure that the manifest has been updated:
Webpacker::Manifest.load("./test/dummy/public/packs/manifest.json")
webpack_manifest = Webpacker::Manifest.instance.data
example_asset_path = webpack_manifest.values.first
if example_asset_path.nil?
# Debug helper
# puts "Manifest is blank, all manifests:"
# Dir.glob("./test/dummy/public/packs/*.json").each do |f|
# puts f
# puts File.read(f)
# end
next
end
# Make sure the dev server is up:
open("http://localhost:8080/application.js")
if !example_asset_path.start_with?("http://localhost:8080")
raise "Manifest doesn't include absolute path to dev server"
end

detected_dev_server = true
break
rescue StandardError => err
puts err.message
ensure
sleep 0.5
# debug counter
# puts i
end
end

# If we didn't hook up with a dev server after waiting, fail loudly.
if !detected_dev_server
raise "Failed to start dev server"
end

# Call the test block:
yield
ensure
# Kill the server process
# puts "Killing webpack dev server"
check_cmd = "lsof -i :8080 -S"
10.times do
# puts check_cmd
status = `#{check_cmd}`
# puts status
remaining_pid_match = status.match(/\n[a-z]+\s+(\d+)/)
if remaining_pid_match
remaining_pid = remaining_pid_match[1]
# puts "Remaining #{remaining_pid}"
kill_cmd = "kill -9 #{remaining_pid}"
# puts kill_cmd
`#{kill_cmd}`
sleep 0.5
else
break
end
end

# Remove the dev-server packs:
WebpackerHelpers.clear_webpacker_packs
# puts "Killed."
end
end