Skip to content

Commit 8c395c9

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 8c395c9

File tree

6 files changed

+113
-24
lines changed

6 files changed

+113
-24
lines changed

lib/react_on_rails/ensure_assets_compiled.rb

+43-10
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,30 @@ 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?
4355

4456
puts "Webpack #{type}-rendering assets built.\n\n"
4557
end
4658
end
59+
60+
class WebpackProcessChecker
61+
def running?
62+
is_running = check_running_for_type("client")
63+
is_running &&= check_running_for_type("server") if Utils.server_rendering_is_enabled?
64+
is_running
65+
end
66+
67+
private
68+
69+
def check_running_for_type(type)
70+
type = type.to_sym
71+
72+
response = `pgrep -fl 'bin/webpack\s\\-w(atch)?\s\\-\\-config\s.*#{type}.*\\.js'`
73+
is_running = Utils.last_process_completed_successfully?
74+
75+
puts "#{type} webpack process is running: #{response.ai}" if is_running
76+
77+
is_running
78+
end
79+
end
4780
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,7 +1,7 @@
11
require_relative "simplecov_helper"
22
require_relative "spec_helper"
33

4-
class WebpackAssetsCompilerDouble
4+
class CompilerDouble
55
attr_reader :times_ran
66

77
def initialize
@@ -13,29 +13,70 @@ def compile
1313
end
1414
end
1515

16+
class ProcessCheckerDouble
17+
attr_accessor :is_running
18+
19+
def initialize(p_is_running)
20+
@is_running = p_is_running
21+
end
22+
23+
def running?
24+
is_running
25+
end
26+
end
27+
28+
class StatusCheckerDouble
29+
attr_accessor :up_to_date
30+
31+
def initialize(initial)
32+
@up_to_date = initial
33+
end
34+
35+
def up_to_date?
36+
up_to_date
37+
end
38+
end
39+
1640
module ReactOnRails
1741
describe EnsureAssetsCompiled do
18-
let(:compiler) { WebpackAssetsCompilerDouble.new }
19-
let(:ensurer) { EnsureAssetsCompiled.new(checker, compiler) }
42+
let(:compiler) { CompilerDouble.new }
43+
let(:ensurer) { EnsureAssetsCompiled.new(assets_checker, compiler, process_checker) }
2044

2145
context "when assets are not up to date" do
22-
let(:checker) { double_webpack_assets_checker(up_to_date: false) }
46+
let(:assets_checker) { StatusCheckerDouble.new(false) }
47+
48+
context "and webpack process is running" do
49+
let(:process_checker) { ProcessCheckerDouble.new(true) }
50+
51+
it "sleeps until assets are up to date" do
52+
thread = Thread.new { ensurer.call }
53+
54+
sleep 1
55+
assets_checker.up_to_date = true
56+
57+
thread.join
2358

24-
it "compiles the webpack bundles" do
25-
expect { ensurer.call }.to change { compiler.times_ran }.from(0).to(1)
59+
expect(compiler.times_ran).to eq(0)
60+
expect(ensurer.assets_have_been_compiled).to eq(true)
61+
end
62+
end
63+
64+
context "and webpack process is NOT running" do
65+
let(:process_checker) { ProcessCheckerDouble.new(false) }
66+
67+
it "compiles the webpack assets" do
68+
expect { ensurer.call }.to change { compiler.times_ran }.from(0).to(1)
69+
end
2670
end
2771
end
2872

2973
context "when assets are up to date" do
30-
let(:checker) { double_webpack_assets_checker(up_to_date: true) }
74+
let(:assets_checker) { StatusCheckerDouble.new(true) }
75+
let(:process_checker) { ProcessCheckerDouble.new(false) }
3176

32-
it "does not compile the webpack bundles if they exist and are up to date" do
77+
it "does nothing" do
3378
expect { ensurer.call }.not_to change { compiler.times_ran }
3479
end
3580
end
36-
37-
def double_webpack_assets_checker(args = {})
38-
instance_double(WebpackAssetsStatusChecker, up_to_date?: args.fetch(:up_to_date))
39-
end
4081
end
4182
end

0 commit comments

Comments
 (0)