Skip to content

Commit c65c1f0

Browse files
committed
Improve EnsureAssetsCompiled logic to wait for webpack process to finish
Because there may be situations where a user is running a webpack process in watch mode in the background that will recompile the assets, it is possible that if the build time is extremely long, the user could start tests before the build completes. In this case, EnsureAssetsCompiled would start compiling the assets again even though there is already a webpack process doing that. This commit adds behavior to EnsureAssetsCompiled to check whether or not the webpack process is running in the background, and will defer the responsibility of compilation to that process, re-checking the compiled assets every second until they are up to date.
1 parent c271a57 commit c65c1f0

File tree

6 files changed

+115
-34
lines changed

6 files changed

+115
-34
lines changed

lib/react_on_rails/ensure_assets_compiled.rb

+57-11
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,44 @@ class EnsureAssetsCompiled
55
def self.build
66
client_dir = Rails.root.join("client")
77
compiled_dirs = COMPILED_DIR_NAMES.map { |dir| Rails.root.join("app", "assets", dir, "generated") }
8-
checker = WebpackAssetsStatusChecker.new(client_dir: client_dir, compiled_dirs: compiled_dirs)
8+
9+
assets_checker = WebpackAssetsStatusChecker.new(client_dir: client_dir, compiled_dirs: compiled_dirs)
10+
process_checker = WebpackProcessChecker.new
911
compiler = WebpackAssetsCompiler.new
10-
new(checker, compiler)
12+
13+
new(assets_checker, compiler, process_checker)
1114
end
1215

13-
attr_reader :webpack_assets_checker, :webpack_assets_compiler, :assets_have_been_compiled
16+
attr_reader :webpack_assets_checker, :webpack_assets_compiler, :webpack_process_checker, :assets_have_been_compiled
1417

15-
def initialize(webpack_assets_checker, webpack_assets_compiler)
18+
def initialize(webpack_assets_checker, webpack_assets_compiler, webpack_process_checker)
1619
@webpack_assets_compiler = webpack_assets_compiler
1720
@webpack_assets_checker = webpack_assets_checker
21+
@webpack_process_checker = webpack_process_checker
1822
@assets_have_been_compiled = false
1923
end
2024

2125
def call
22-
should_skip_compiling = assets_have_been_compiled || @webpack_assets_checker.up_to_date?
23-
webpack_assets_compiler.compile unless should_skip_compiling
26+
loop do
27+
should_skip_compiling = assets_have_been_compiled || @webpack_assets_checker.up_to_date?
28+
break if should_skip_compiling
29+
30+
if webpack_process_checker.running?
31+
sleep 1
32+
else
33+
webpack_assets_compiler.compile
34+
break
35+
end
36+
end
37+
2438
@assets_have_been_compiled = true
2539
end
2640
end
2741

2842
class WebpackAssetsCompiler
2943
def compile
3044
compile_type(:client)
31-
compile_type(:server) if ReactOnRails.configuration.server_bundle_js_file.present?
45+
compile_type(:server) if Utils.server_rendering_is_enabled?
3246
end
3347

3448
private
@@ -37,11 +51,43 @@ def compile_type(type)
3751
puts "\n\nBuilding Webpack #{type}-rendering assets..."
3852
build_output = `cd client && npm run build:#{type}`
3953

40-
if build_output =~ /error/i
41-
fail "Error in building assets!\n#{build_output}"
42-
end
54+
fail "Error in building assets!\n#{build_output}" unless Utils.last_process_completed_successfully?
55+
56+
puts "Webpack #{type}-rendering assets built. If you are frequently running\n"\
57+
"tests, you can run webpack in watch mode to speed up this process.\n\n"
58+
end
59+
end
60+
61+
class WebpackProcessChecker
62+
def running?
63+
client_running = check_running_for_type("client")
64+
return client_running unless Utils.server_rendering_is_enabled?
65+
66+
server_running = check_running_for_type("server")
67+
68+
fail_if_only_running_for_one_type(client_running, server_running)
69+
70+
client_running && server_running
71+
end
72+
73+
private
74+
75+
def fail_if_only_running_for_one_type(client_running, server_running)
76+
return unless client_running ^ server_running
77+
fail "\n\nError: detected webpack is not running for both types of assets:\n"\
78+
"***Webpack Client Process Running?: #{client_running}\n"\
79+
"***Webpack Server Process Running?: #{server_running}"
80+
end
81+
82+
def check_running_for_type(type)
83+
type = type.to_sym
84+
85+
response = `pgrep -fl 'bin/webpack\s(\\-w|\\-\\-watch)\s\\-\\-config\s.*#{type}.*\\.js'`
86+
is_running = Utils.last_process_completed_successfully?
87+
88+
puts "#{type} webpack process is running: #{response.ai}" if is_running
4389

44-
puts "Webpack #{type}-rendering assets built.\n\n"
90+
is_running
4591
end
4692
end
4793
end

lib/react_on_rails/utils.rb

+10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
1+
require "english"
2+
13
module ReactOnRails
24
module Utils
35
def self.object_to_boolean(value)
46
[true, "true", "yes", 1, "1", "t"].include?(value.class == String ? value.downcase : value)
57
end
8+
9+
def self.server_rendering_is_enabled?
10+
ReactOnRails.configuration.server_bundle_js_file.present?
11+
end
12+
13+
def self.last_process_completed_successfully?
14+
$CHILD_STATUS.exitstatus == 0
15+
end
616
end
717
end

lib/react_on_rails/webpack_assets_status_checker.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ def up_to_date?
1717
private
1818

1919
def all_compiled_assets
20-
@all_compiled_assets ||= make_file_list(make_globs(compiled_dirs)).to_ary
20+
@all_compiled_assets = make_file_list(make_globs(compiled_dirs)).to_ary
2121
end
2222

2323
def client_files
24-
@client_files ||= make_file_list(make_globs(client_dir)).to_ary
24+
@client_files = make_file_list(make_globs(client_dir)).to_ary
2525
end
2626

2727
def make_globs(dirs)

spec/dummy/Gemfile.lock

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ PATH
44
react_on_rails (2.3.0)
55
connection_pool
66
execjs (~> 2.5)
7+
foreman
78
rails (>= 3.2)
89
rainbow (~> 2.1)
910

@@ -105,6 +106,8 @@ GEM
105106
erubis (2.7.0)
106107
execjs (2.6.0)
107108
ffi (1.9.10)
109+
foreman (0.78.0)
110+
thor (~> 0.19.1)
108111
generator_spec (0.9.3)
109112
activesupport (>= 3.0.0)
110113
railties (>= 3.0.0)

spec/dummy/Procfile.spec

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
client-static-assets: sh -c 'rm app/assets/javascripts/generated/* || true && cd client && npm run build:dev:client'
2+
server-static-assets: sh -c 'cd client && npm run build:dev:server'
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,61 @@
11
require_relative "simplecov_helper"
22
require_relative "spec_helper"
33

4-
class WebpackAssetsCompilerDouble
5-
attr_reader :times_ran
6-
7-
def initialize
8-
@times_ran = 0
9-
end
10-
11-
def compile
12-
@times_ran += 1
13-
end
14-
end
15-
164
module ReactOnRails
175
describe EnsureAssetsCompiled do
18-
let(:compiler) { WebpackAssetsCompilerDouble.new }
19-
let(:ensurer) { EnsureAssetsCompiled.new(checker, compiler) }
6+
let(:compiler) { double_assets_compiler }
7+
let(:ensurer) { EnsureAssetsCompiled.new(assets_checker, compiler, process_checker) }
208

219
context "when assets are not up to date" do
22-
let(:checker) { double_webpack_assets_checker(up_to_date: false) }
10+
let(:assets_checker) { double_assets_checker(up_to_date: false) }
11+
12+
context "and webpack process is running" do
13+
let(:process_checker) { double_process_checker(running: true) }
14+
15+
it "sleeps until assets are up to date" do
16+
expect(compiler).not_to receive(:compile)
17+
18+
thread = Thread.new { ensurer.call }
2319

24-
it "compiles the webpack bundles" do
25-
expect { ensurer.call }.to change { compiler.times_ran }.from(0).to(1)
20+
sleep 1
21+
allow(assets_checker).to receive(:up_to_date?).and_return(true)
22+
23+
thread.join
24+
25+
expect(ensurer.assets_have_been_compiled).to eq(true)
26+
end
27+
end
28+
29+
context "and webpack process is NOT running" do
30+
let(:process_checker) { double_process_checker(running: false) }
31+
32+
it "compiles the webpack assets" do
33+
expect(compiler).to receive(:compile).once
34+
ensurer.call
35+
end
2636
end
2737
end
2838

2939
context "when assets are up to date" do
30-
let(:checker) { double_webpack_assets_checker(up_to_date: true) }
40+
let(:assets_checker) { double_assets_checker(up_to_date: true) }
41+
let(:process_checker) { double_process_checker(running: false) }
3142

32-
it "does not compile the webpack bundles if they exist and are up to date" do
33-
expect { ensurer.call }.not_to change { compiler.times_ran }
43+
it "does nothing" do
44+
expect(compiler).not_to receive(:compile)
45+
ensurer.call
3446
end
3547
end
3648

37-
def double_webpack_assets_checker(args = {})
49+
def double_process_checker(args = {})
50+
instance_double(WebpackProcessChecker, running?: args.fetch(:running))
51+
end
52+
53+
def double_assets_checker(args = {})
3854
instance_double(WebpackAssetsStatusChecker, up_to_date?: args.fetch(:up_to_date))
3955
end
56+
57+
def double_assets_compiler
58+
instance_double(WebpackAssetsCompiler, :compile)
59+
end
4060
end
4161
end

0 commit comments

Comments
 (0)