Skip to content

Commit 265dfe1

Browse files
justin808claude
andcommitted
Write [Shakapacker] log messages to stderr instead of stdout
Fixes #868. When using `bin/shakapacker --profile --json`, the [Shakapacker] log messages were being written to stdout, which broke JSON parsing tools like webpack-bundle-analyzer. Changed all informational log messages (prefixed with [Shakapacker]) to use $stderr.puts instead of puts, so that: - stdout remains clean for JSON output from webpack/rspack - log messages are still visible to users on stderr - tools that parse stdout get valid JSON Updated test files to capture stderr instead of stdout for tests that verify log output. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent ab57060 commit 265dfe1

File tree

5 files changed

+49
-58
lines changed

5 files changed

+49
-58
lines changed

lib/shakapacker/dev_server_runner.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,10 @@ def self.run_with_build_config(argv, build_config)
7878
# This ensures the bundler override (from --bundler or build config) is respected
7979
ENV["SHAKAPACKER_ASSETS_BUNDLER"] = build_config[:bundler]
8080

81-
puts "[Shakapacker] Running dev server for build: #{build_config[:name]}"
82-
puts "[Shakapacker] Description: #{build_config[:description]}" if build_config[:description]
83-
puts "[Shakapacker] Bundler: #{build_config[:bundler]}"
84-
puts "[Shakapacker] Config file: #{build_config[:config_file]}" if build_config[:config_file]
81+
$stderr.puts "[Shakapacker] Running dev server for build: #{build_config[:name]}"
82+
$stderr.puts "[Shakapacker] Description: #{build_config[:description]}" if build_config[:description]
83+
$stderr.puts "[Shakapacker] Bundler: #{build_config[:bundler]}"
84+
$stderr.puts "[Shakapacker] Config file: #{build_config[:config_file]}" if build_config[:config_file]
8585

8686
# Pass bundler override so Configuration.assets_bundler reflects the build
8787
new(argv, build_config, build_config[:bundler]).run

lib/shakapacker/runner.rb

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,9 @@ def self.run(argv)
9999

100100
# If this build uses dev server, delegate to DevServerRunner
101101
if loader.uses_dev_server?(build_config)
102-
$stdout.puts "[Shakapacker] Build '#{build_name}' requires dev server"
103-
$stdout.puts "[Shakapacker] Running: bin/shakapacker-dev-server --build #{build_name}"
104-
$stdout.puts ""
102+
$stderr.puts "[Shakapacker] Build '#{build_name}' requires dev server"
103+
$stderr.puts "[Shakapacker] Running: bin/shakapacker-dev-server --build #{build_name}"
104+
$stderr.puts ""
105105
require_relative "dev_server_runner"
106106
DevServerRunner.run_with_build_config(remaining_argv, build_config)
107107
return
@@ -179,10 +179,10 @@ def self.run_with_build_config(argv, build_config)
179179
# This ensures the bundler override (from --bundler or build config) is respected
180180
ENV["SHAKAPACKER_ASSETS_BUNDLER"] = build_config[:bundler]
181181

182-
puts "[Shakapacker] Running build: #{build_config[:name]}"
183-
puts "[Shakapacker] Description: #{build_config[:description]}" if build_config[:description]
184-
puts "[Shakapacker] Bundler: #{build_config[:bundler]}"
185-
puts "[Shakapacker] Config file: #{build_config[:config_file]}" if build_config[:config_file]
182+
$stderr.puts "[Shakapacker] Running build: #{build_config[:name]}"
183+
$stderr.puts "[Shakapacker] Description: #{build_config[:description]}" if build_config[:description]
184+
$stderr.puts "[Shakapacker] Bundler: #{build_config[:bundler]}"
185+
$stderr.puts "[Shakapacker] Config file: #{build_config[:config_file]}" if build_config[:config_file]
186186

187187
# Create runner with modified argv and bundler from build_config
188188
# The build_config[:bundler] already has any CLI --bundler override applied
@@ -240,40 +240,40 @@ def package_json
240240
end
241241

242242
def run
243-
puts "[Shakapacker] Preparing environment for assets bundler execution..."
243+
$stderr.puts "[Shakapacker] Preparing environment for assets bundler execution..."
244244
env = Shakapacker::Compiler.env
245245
env["SHAKAPACKER_CONFIG"] = @shakapacker_config
246246
env["NODE_OPTIONS"] = ENV["NODE_OPTIONS"] || ""
247247

248248
cmd = build_cmd
249-
puts "[Shakapacker] Base command: #{cmd.join(" ")}"
249+
$stderr.puts "[Shakapacker] Base command: #{cmd.join(" ")}"
250250

251251
if @argv.delete("--debug-shakapacker")
252-
puts "[Shakapacker] Debug mode enabled (--debug-shakapacker)"
252+
$stderr.puts "[Shakapacker] Debug mode enabled (--debug-shakapacker)"
253253
env["NODE_OPTIONS"] = "#{env["NODE_OPTIONS"]} --inspect-brk"
254254
end
255255

256256
if @argv.delete "--trace-deprecation"
257-
puts "[Shakapacker] Trace deprecation enabled (--trace-deprecation)"
257+
$stderr.puts "[Shakapacker] Trace deprecation enabled (--trace-deprecation)"
258258
env["NODE_OPTIONS"] = "#{env["NODE_OPTIONS"]} --trace-deprecation"
259259
end
260260

261261
if @argv.delete "--no-deprecation"
262-
puts "[Shakapacker] Deprecation warnings disabled (--no-deprecation)"
262+
$stderr.puts "[Shakapacker] Deprecation warnings disabled (--no-deprecation)"
263263
env["NODE_OPTIONS"] = "#{env["NODE_OPTIONS"]} --no-deprecation"
264264
end
265265

266266
# Commands are not compatible with --config option.
267267
if (@argv & assets_bundler_commands).empty?
268-
puts "[Shakapacker] Adding config file: #{@webpack_config}"
268+
$stderr.puts "[Shakapacker] Adding config file: #{@webpack_config}"
269269
cmd += ["--config", @webpack_config]
270270
else
271-
puts "[Shakapacker] Skipping config file (running assets bundler command: #{(@argv & assets_bundler_commands).join(", ")})"
271+
$stderr.puts "[Shakapacker] Skipping config file (running assets bundler command: #{(@argv & assets_bundler_commands).join(", ")})"
272272
end
273273

274274
cmd += @argv
275-
puts "[Shakapacker] Final command: #{cmd.join(" ")}"
276-
puts "[Shakapacker] Working directory: #{@app_path}"
275+
$stderr.puts "[Shakapacker] Final command: #{cmd.join(" ")}"
276+
$stderr.puts "[Shakapacker] Working directory: #{@app_path}"
277277

278278
watch_mode = @argv.include?("--watch") || @argv.include?("-w")
279279
start_time = Time.now unless watch_mode
@@ -288,7 +288,7 @@ def run
288288
minutes = (elapsed_time / 60).floor
289289
seconds = (elapsed_time % 60).round(2)
290290
time_display = minutes > 0 ? "#{minutes}:#{format('%05.2f', seconds)}s" : "#{elapsed_time.round(2)}s"
291-
puts "[Shakapacker] Completed #{bundler_name} build in #{time_display} (#{elapsed_time.round(2)}s)"
291+
$stderr.puts "[Shakapacker] Completed #{bundler_name} build in #{time_display} (#{elapsed_time.round(2)}s)"
292292
end
293293
exit($?.exitstatus || 1) unless $?.success?
294294
end
@@ -601,10 +601,10 @@ def find_rspack_config_with_fallback
601601
File.join(@app_path, config_dir, "rspack.config.#{ext}")
602602
end
603603

604-
puts "[Shakapacker] Looking for Rspack config in: #{rspack_paths.join(", ")}"
604+
$stderr.puts "[Shakapacker] Looking for Rspack config in: #{rspack_paths.join(", ")}"
605605
rspack_path = rspack_paths.find { |f| File.exist?(f) }
606606
if rspack_path
607-
puts "[Shakapacker] Found Rspack config: #{rspack_path}"
607+
$stderr.puts "[Shakapacker] Found Rspack config: #{rspack_path}"
608608
return rspack_path
609609
end
610610

@@ -613,7 +613,7 @@ def find_rspack_config_with_fallback
613613
File.join(@app_path, config_dir, "webpack.config.#{ext}")
614614
end
615615

616-
puts "[Shakapacker] Rspack config not found, checking for webpack config fallback..."
616+
$stderr.puts "[Shakapacker] Rspack config not found, checking for webpack config fallback..."
617617
webpack_path = webpack_paths.find { |f| File.exist?(f) }
618618
if webpack_path
619619
$stderr.puts "⚠️ DEPRECATION WARNING: Using webpack config file for Rspack assets bundler."
@@ -629,7 +629,7 @@ def find_rspack_config_with_fallback
629629
File.join(@app_path, "config/webpack", "webpack.config.#{ext}")
630630
end
631631

632-
puts "[Shakapacker] Checking config/webpack/ for backward compatibility..."
632+
$stderr.puts "[Shakapacker] Checking config/webpack/ for backward compatibility..."
633633
webpack_dir_path = webpack_dir_paths.find { |f| File.exist?(f) }
634634
if webpack_dir_path
635635
$stderr.puts "⚠️ DEPRECATION WARNING: Found webpack config in config/webpack/ but assets_bundler is set to 'rspack'."
@@ -656,13 +656,13 @@ def find_webpack_config
656656
possible_paths = %w[ts js].map do |ext|
657657
File.join(@app_path, config_dir, "webpack.config.#{ext}")
658658
end
659-
puts "[Shakapacker] Looking for Webpack config in: #{possible_paths.join(", ")}"
659+
$stderr.puts "[Shakapacker] Looking for Webpack config in: #{possible_paths.join(", ")}"
660660
path = possible_paths.find { |f| File.exist?(f) }
661661
unless path
662662
print_config_not_found_error("webpack", possible_paths.last, config_dir)
663663
exit(1)
664664
end
665-
puts "[Shakapacker] Found Webpack config: #{path}"
665+
$stderr.puts "[Shakapacker] Found Webpack config: #{path}"
666666
path
667667
end
668668
end

spec/shakapacker/rspack_runner_spec.rb

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@
143143
true
144144
end
145145

146-
output = capture_stdout { klass.run([]) }
146+
output = capture_stderr { klass.run([]) }
147147

148148
# The test app may have webpack config, so bundler name could be either
149149
# Time format can be either "X.XXs" or "M:SS.SSs" for the display, always "X.XXs" in parentheses
@@ -164,7 +164,7 @@
164164
true
165165
end
166166

167-
output = capture_stdout { klass.run(["--watch"]) }
167+
output = capture_stderr { klass.run(["--watch"]) }
168168

169169
expect(output).not_to match(/Completed (webpack|rspack) build/)
170170
end
@@ -183,7 +183,7 @@
183183
true
184184
end
185185

186-
output = capture_stdout { klass.run(["-w"]) }
186+
output = capture_stderr { klass.run(["-w"]) }
187187

188188
expect(output).not_to match(/Completed (webpack|rspack) build/)
189189
end
@@ -192,13 +192,13 @@
192192

193193
private
194194

195-
def capture_stdout
196-
old_stdout = $stdout
197-
$stdout = StringIO.new
195+
def capture_stderr
196+
old_stderr = $stderr
197+
$stderr = StringIO.new
198198
yield
199-
$stdout.string
199+
$stderr.string
200200
ensure
201-
$stdout = old_stdout
201+
$stderr = old_stderr
202202
end
203203

204204
def verify_command(cmd, argv: [])

spec/shakapacker/runner_build_config_spec.rb

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
allow(klass).to receive(:new).and_return(instance)
4949
allow(instance).to receive(:system).and_return(true)
5050

51-
output = capture_stdout do
51+
output = capture_stderr do
5252
klass.run(["--build", "prod"])
5353
end
5454

@@ -85,7 +85,7 @@
8585
allow(dev_server_klass).to receive(:new).and_return(dev_server_instance)
8686
allow(dev_server_instance).to receive(:run).and_return(nil)
8787

88-
output = capture_stdout do
88+
output = capture_stderr do
8989
klass.run(["--build", "dev-hmr"])
9090
end
9191

@@ -114,7 +114,7 @@
114114
allow(klass).to receive(:new).and_return(instance)
115115
allow(instance).to receive(:system).and_return(true)
116116

117-
output = capture_stdout do
117+
output = capture_stderr do
118118
klass.run(["nonexistent"])
119119
end
120120

@@ -132,7 +132,7 @@
132132
allow(klass).to receive(:new).and_return(instance)
133133
allow(instance).to receive(:system).and_return(true)
134134

135-
output = capture_stdout do
135+
output = capture_stderr do
136136
klass.run([])
137137
end
138138

@@ -198,7 +198,7 @@
198198
allow(klass).to receive(:new).and_return(instance)
199199
allow(instance).to receive(:run).and_return(nil)
200200

201-
output = capture_stdout do
201+
output = capture_stderr do
202202
klass.run(["--build", "dev"])
203203
end
204204

@@ -264,7 +264,7 @@
264264
allow(dev_server_klass).to receive(:new).and_return(dev_server_instance)
265265
allow(dev_server_instance).to receive(:run).and_return(nil)
266266

267-
output = capture_stdout do
267+
output = capture_stderr do
268268
klass.run(["--build", "dev-hmr"])
269269
end
270270

@@ -277,15 +277,6 @@
277277

278278
private
279279

280-
def capture_stdout
281-
old_stdout = $stdout
282-
$stdout = StringIO.new
283-
yield
284-
$stdout.string
285-
ensure
286-
$stdout = old_stdout
287-
end
288-
289280
def capture_stderr
290281
old_stderr = $stderr
291282
$stderr = StringIO.new

spec/shakapacker/webpack_runner_spec.rb

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@
158158
true
159159
end
160160

161-
output = capture_stdout { klass.run([]) }
161+
output = capture_stderr { klass.run([]) }
162162

163163
# Time format can be either "X.XXs" or "M:SS.SSs" for the display, always "X.XXs" in parentheses
164164
expect(output).to match(/\[Shakapacker\] Completed webpack build in (\d+:\d+\.\d+s|\d+\.\d+s) \(\d+\.\d+s\)/)
@@ -178,7 +178,7 @@
178178
true
179179
end
180180

181-
output = capture_stdout { klass.run(["--watch"]) }
181+
output = capture_stderr { klass.run(["--watch"]) }
182182

183183
expect(output).not_to match(/Completed webpack build/)
184184
end
@@ -197,7 +197,7 @@
197197
true
198198
end
199199

200-
output = capture_stdout { klass.run([]) }
200+
output = capture_stderr { klass.run([]) }
201201

202202
# Should show format like "3.29s (3.29s)" without minutes
203203
expect(output).to match(/\[Shakapacker\] Completed webpack build in \d+\.\d+s \(\d+\.\d+s\)/)
@@ -208,13 +208,13 @@
208208

209209
private
210210

211-
def capture_stdout
212-
old_stdout = $stdout
213-
$stdout = StringIO.new
211+
def capture_stderr
212+
old_stderr = $stderr
213+
$stderr = StringIO.new
214214
yield
215-
$stdout.string
215+
$stderr.string
216216
ensure
217-
$stdout = old_stdout
217+
$stderr = old_stderr
218218
end
219219

220220
def verify_command(cmd, argv: [])

0 commit comments

Comments
 (0)