Skip to content

Commit 0e40c8b

Browse files
justin808claude
andauthored
Fix legacy JS binstub dispatch and NODE_ENV default in helper binstubs (#1127)
## Summary Address the **high-priority bug fixes** from the [PR #1104 follow-up review](#1123) on ESM-safe config binstubs. This is **PR 1 of 3** for #1123 — focused on user-visible regressions. Test-coverage and polish items will follow in separate PRs to keep review scope tight. ### Fixes - **#1 — rake task crash for upgraded apps with a legacy JS binstub.** `shakapacker:export_bundler_config` previously invoked `bin/shakapacker-config` via `RbConfig.ruby` unconditionally. Apps upgraded from older Shakapacker versions still carry a `#!/usr/bin/env node` JavaScript binstub that this gem did not rewrite — running it through Ruby would try to parse JS as Ruby and crash. The task now detects the shebang and dispatches: Ruby binstubs keep the version-manager protection from #1104; legacy JS binstubs are exec'd directly so their shebang resolves Node, and the user is nudged to run `rake shakapacker:binstubs` to upgrade. A new `Shakapacker::Utils::Misc.js_binstub?` helper makes the detection unit-testable. - **#2 — `NODE_ENV` default ignored `RAILS_ENV` in helper binstubs.** The helper binstubs hard-coded `ENV["NODE_ENV"] ||= "development"`, which diverged from `Shakapacker.ensure_node_env!`. Concretely, `RAILS_ENV=production bin/shakapacker-config show-config` with `NODE_ENV` unset spawned Node with `NODE_ENV=development` and produced the development bundler config. The default now mirrors the Ruby logic (`DEV_ENVS = %w[development test]`): only development and test default to `NODE_ENV=development`; everything else defaults to `production`. Applied in all four sync'd copies: `lib/install/bin/shakapacker-config`, `lib/install/bin/diff-bundler-config`, `spec/dummy/bin/shakapacker-config`, and the `createBinStub` template in `package/configExporter/cli.ts`. - **#6 — `error.message` guard.** `package/bin/shakapacker-config.cjs` and `bin/shakapacker-config` now stringify non-`Error` throws (`error instanceof Error ? error.message : String(error)`) instead of printing `undefined`, matching the existing `formatError` pattern in `diff-bundler-config.cjs`. - **#7 — `NODE_ENV` default in `diff-bundler-config`.** Resolved by #2's fix: kept the default (now using the corrected logic) with a comment explaining why it stays consistent with `shakapacker-config`. ### Follow-up PRs Items deferred to keep this PR focused: - **PR 2** — test-coverage gaps: dummy binstub sync coverage (#3), `createBinStub` template sync spec (#4), fallback-warning assertion (#5), "Keep in sync" comments updated to list all four locations (#8), `binstub_sync_spec` test rename (#9). - **PR 3** — polish: replace `system(node, "--version")` probe with a no-fork PATH walk (#10). Refs #1123. ## Test plan - [x] `bundle exec rspec spec/shakapacker/utils_misc_spec.rb` — new unit tests for `js_binstub?` (8 examples) - [x] `bundle exec rspec spec/shakapacker/helper_binstubs_spec.rb` — existing helper binstub specs still pass (8 examples) - [x] `bundle exec rspec spec/shakapacker/binstub_sync_spec.rb` — sync spec still passes - [x] `bundle exec rspec spec/shakapacker/doctor_spec.rb` — doctor specs (147 examples) still pass - [x] `bundle exec rubocop` — no offenses on changed Ruby files - [x] `yarn eslint` — no errors on changed JS/TS files - [x] `ruby -c` on all modified Ruby binstubs/rake task — syntax OK 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Medium risk because it changes how rake tasks and binstubs choose interpreters and default environment variables, which can affect build/config export behavior across environments. > > **Overview** > **Fixes config-export helper regressions for upgraded apps.** `shakapacker:export_bundler_config` now detects legacy `#!/usr/bin/env node` `bin/shakapacker-config` shims and execs them directly (while still running modern Ruby binstubs via `RbConfig.ruby`), avoiding crashes and prompting users to regenerate binstubs. > > **Aligns environment defaults across helper binstubs.** `shakapacker-config`/`diff-bundler-config` now default `NODE_ENV` to `development` only for `RAILS_ENV` in `development|test`, otherwise `production`, and both Node shims now stringify non-`Error` throws for clearer stderr. Adds a unit-tested `Shakapacker::Utils::Misc.js_binstub?` helper and updates the changelog. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 08c3c39. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Fixed a regression when exporting bundler config after upgrades from older installations. * Improved detection of legacy JavaScript helper scripts (including `env`-style shebangs) and now runs the appropriate Node or Ruby interpreter. * Enhanced error handling and messages when the JavaScript or Ruby runtime can’t be executed, including upgrade guidance to refresh helper scripts. * **Tests** * Expanded coverage for helper-script interpreter detection, guidance output, and failure scenarios (including missing runtimes and varied shebang formats). <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent e951b98 commit 0e40c8b

5 files changed

Lines changed: 292 additions & 22 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
### Fixed
2323

2424
- **Fixed dev-server passthrough argument validation messages**. [PR #1180](https://github.com/shakacode/shakapacker/pull/1180) by [justin808](https://github.com/justin808). `bin/shakapacker-dev-server -- --host ...` or `--port ...` now fails with a clearer Shakapacker message instead of forwarding the separator to the bundler; set `dev_server.host` and `dev_server.port` in `config/shakapacker.yml` instead.
25+
- **Fixed `shakapacker:export_bundler_config` regression on apps upgraded from older Shakapacker versions**. [PR #1127](https://github.com/shakacode/shakapacker/pull/1127) by [justin808](https://github.com/justin808). The task previously invoked `bin/shakapacker-config` unconditionally via `RbConfig.ruby`, which crashed when the file was still the legacy JavaScript binstub (`#!/usr/bin/env node`) left over from earlier installs. The task now inspects the shebang and exec's the file directly when it points at Node, while keeping the Ruby+`RbConfig.ruby` path for the current Ruby binstub. Affected users are also nudged to run `bundle exec rake shakapacker:binstubs` to refresh their helper binstub. Refs [#1123](https://github.com/shakacode/shakapacker/issues/1123).
2526
- **Fixed `shakapacker:doctor` Sass implementation detection for `sass-embedded` apps**. [PR #1178](https://github.com/shakacode/shakapacker/pull/1178) by [justin808](https://github.com/justin808). The doctor now accepts either modern Sass implementation package (`sass` or `sass-embedded`) when Sass files are present, matching Shakapacker's default modern Sass API instead of incorrectly requiring Dart Sass. New installs also include `sass` alongside `sass-loader`. Fixes [#1172](https://github.com/shakacode/shakapacker/issues/1172).
2627
- **Fixed compiler strategies ignoring the instance config of custom `Shakapacker::Instance` objects.** [PR #1147](https://github.com/shakacode/shakapacker/pull/1147) by [justin808](https://github.com/justin808). Ports [#976](https://github.com/shakacode/shakapacker/pull/976) by [brunodccarvalho](https://github.com/brunodccarvalho). Strategies now read the instance-specific config and watch both webpack and rspack config directories.
2728

lib/shakapacker/utils/misc.rb

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,18 @@
22

33
require "English"
44
require "rake/file_utils"
5+
require "shellwords"
56

67
module Shakapacker
78
module Utils
89
class Misc
910
extend FileUtils
1011

12+
NODE_BINSTUB_EXECUTABLES = %w[node nodejs].freeze
13+
ENV_FLAGS_WITH_ARGUMENTS = %w[-u --unset -C --chdir -P --path -a --argv0].freeze
14+
private_constant :NODE_BINSTUB_EXECUTABLES
15+
private_constant :ENV_FLAGS_WITH_ARGUMENTS
16+
1117
def self.uncommitted_changes?(message_handler)
1218
return false if ENV["COVERAGE"] == "true"
1319

@@ -31,6 +37,38 @@ def self.object_to_boolean(value)
3137
def self.sh_in_dir(dir, *shell_commands)
3238
shell_commands.flatten.each { |shell_command| sh %(cd '#{dir}' && #{shell_command.strip}) }
3339
end
40+
41+
def self.js_binstub_executable(path)
42+
return nil unless File.file?(path)
43+
44+
shebang = File.open(path, "rb") { |f| f.gets }.to_s.chomp
45+
return nil unless shebang.start_with?("#!")
46+
47+
shebang_tokens = begin
48+
Shellwords.split(shebang.delete_prefix("#!"))
49+
rescue ArgumentError
50+
return nil
51+
end
52+
executable = shebang_tokens.first.to_s
53+
54+
if File.basename(executable) == "env"
55+
shebang_tokens = shebang_tokens.drop(1)
56+
while (env_token = shebang_tokens.first)
57+
if env_token.start_with?("-")
58+
env_flag = shebang_tokens.shift
59+
shebang_tokens.shift if ENV_FLAGS_WITH_ARGUMENTS.include?(env_flag)
60+
elsif env_token.match?(/\A[A-Za-z_][A-Za-z0-9_]*=/)
61+
shebang_tokens.shift
62+
else
63+
break
64+
end
65+
end
66+
executable = shebang_tokens.first.to_s
67+
end
68+
69+
# Preserve direct interpreter paths so stale absolute Node shebangs fail with actionable binstub guidance.
70+
NODE_BINSTUB_EXECUTABLES.include?(File.basename(executable)) ? executable : nil
71+
end
3472
end
3573
end
3674
end

lib/tasks/shakapacker/export_bundler_config.rake

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,6 @@
1-
namespace :shakapacker do
2-
def shakapacker_config_binstub_command(bin_path)
3-
# Read in binary mode so Windows CRLF line endings do not leak \r into the shebang.
4-
shebang = File.open(bin_path, "rb", &:gets).to_s
5-
command = shebang.delete_prefix("#!").strip.split(/\s+/)
6-
executable = File.basename(command.first.to_s)
7-
8-
if executable == "env"
9-
executable = File.basename(command.drop(1).find { |part| !part.start_with?("-") }.to_s)
10-
end
11-
12-
# Legacy JS binstubs are dispatched via PATH lookup; Kernel#exec resolves
13-
# "node" through the shell's PATH. The Ruby binstubs perform their own
14-
# explicit lookup via shakapacker_find_executable, which matters more on
15-
# Windows where PATHEXT is involved.
16-
executable == "node" ? ["node", bin_path.to_s] : [RbConfig.ruby, bin_path.to_s]
17-
end
1+
require "shakapacker/utils/misc"
182

3+
namespace :shakapacker do
194
desc <<~DESC
205
Export webpack or rspack configuration for debugging and analysis
216
@@ -62,7 +47,7 @@ namespace :shakapacker do
6247
# Try to use the binstub if it exists, otherwise use the gem's version
6348
bin_path = Rails.root.join("bin/shakapacker-config")
6449

65-
unless File.exist?(bin_path)
50+
unless File.file?(bin_path)
6651
# Binstub not installed, use the gem's version directly
6752
gem_bin_path = File.expand_path("../../install/bin/shakapacker-config", __dir__)
6853

@@ -75,10 +60,34 @@ namespace :shakapacker do
7560
end
7661
else
7762
# Pass through command-line arguments after the task name.
78-
# Ruby binstubs run under the same Ruby as Rake; legacy JavaScript
79-
# binstubs from upgraded apps still need Node until users refresh them.
63+
#
64+
# Modern Ruby binstubs (the current default) are invoked with
65+
# RbConfig.ruby so they run under the same Ruby as Rake — this avoids
66+
# version-manager/shebang mismatches and works on Windows.
67+
#
68+
# Legacy JavaScript binstubs left over from earlier Shakapacker
69+
# versions (`#!/usr/bin/env node`) are invoked through Node until
70+
# users refresh them with `rake shakapacker:binstubs`.
71+
js_binstub_executable = Shakapacker::Utils::Misc.js_binstub_executable(bin_path)
72+
8073
Dir.chdir(Rails.root) do
81-
Kernel.exec(*shakapacker_config_binstub_command(bin_path), *ARGV[1..])
74+
if js_binstub_executable
75+
$stderr.puts "Note: bin/shakapacker-config is a legacy JavaScript binstub."
76+
$stderr.puts "Run `bundle exec rake shakapacker:binstubs` to upgrade to the Ruby binstub."
77+
$stderr.puts ""
78+
begin
79+
Kernel.exec(js_binstub_executable, bin_path.to_s, *ARGV[1..])
80+
rescue Errno::ENOENT, Errno::EACCES
81+
abort "Error: could not execute '#{js_binstub_executable}' because it was not found or is not executable.\n" \
82+
"Run `bundle exec rake shakapacker:binstubs` to upgrade to the Ruby binstub."
83+
end
84+
else
85+
begin
86+
Kernel.exec(RbConfig.ruby, bin_path.to_s, *ARGV[1..])
87+
rescue Errno::ENOENT, Errno::EACCES
88+
abort "Error: Ruby interpreter '#{RbConfig.ruby}' was not found or is not executable."
89+
end
90+
end
8291
end
8392
end
8493
end

spec/shakapacker/export_bundler_config_task_spec.rb

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
require "pathname"
33
require "rake"
44
require "rbconfig"
5+
require "stringio"
56
require "tmpdir"
67

78
RSpec.describe "shakapacker:export_bundler_config" do
@@ -29,10 +30,12 @@ def write_app_binstub(app_path, content)
2930
binstub_path
3031
end
3132

32-
def invoke_task(app_path, *args)
33+
def invoke_task(app_path, *args, exec_error: nil)
3334
captured_exec = nil
3435
stub_const("Rails", double(root: Pathname.new(app_path)))
3536
allow(Kernel).to receive(:exec) do |*exec_args|
37+
raise exec_error if exec_error
38+
3639
captured_exec = exec_args
3740
end
3841

@@ -42,14 +45,89 @@ def invoke_task(app_path, *args)
4245
captured_exec
4346
end
4447

48+
def capture_stderr
49+
stderr_output = StringIO.new
50+
original_stderr = $stderr
51+
$stderr = stderr_output
52+
yield stderr_output
53+
ensure
54+
$stderr = original_stderr
55+
end
56+
4557
it "runs upgraded apps' legacy JavaScript binstub with node" do
4658
Dir.mktmpdir("shakapacker-export-bundler-config-") do |app_path|
4759
binstub_path = write_app_binstub(app_path, <<~JS)
4860
#!/usr/bin/env node
4961
console.log("legacy binstub")
5062
JS
5163

64+
allow($stderr).to receive(:puts)
65+
5266
expect(invoke_task(app_path, "--doctor")).to eq(["node", binstub_path, "--doctor"])
67+
expect($stderr).to have_received(:puts).with(a_string_including("legacy JavaScript binstub"))
68+
expect($stderr).to have_received(:puts).with(a_string_including("rake shakapacker:binstubs"))
69+
end
70+
end
71+
72+
it "runs nodejs legacy binstubs with nodejs and warns about the legacy binstub" do
73+
Dir.mktmpdir("shakapacker-export-bundler-config-") do |app_path|
74+
binstub_path = write_app_binstub(app_path, <<~JS)
75+
#!/usr/bin/env nodejs
76+
console.log("legacy binstub")
77+
JS
78+
79+
allow($stderr).to receive(:puts)
80+
81+
expect(invoke_task(app_path, "--doctor")).to eq(
82+
["nodejs", binstub_path, "--doctor"]
83+
)
84+
expect($stderr).to have_received(:puts).with(a_string_including("legacy JavaScript binstub"))
85+
expect($stderr).to have_received(:puts).with(a_string_including("rake shakapacker:binstubs"))
86+
end
87+
end
88+
89+
it "aborts with upgrade guidance when a legacy JavaScript binstub executable is missing" do
90+
Dir.mktmpdir("shakapacker-export-bundler-config-") do |app_path|
91+
write_app_binstub(app_path, <<~JS)
92+
#!/usr/bin/env node
93+
console.log("legacy binstub")
94+
JS
95+
96+
capture_stderr do |stderr_output|
97+
expect { invoke_task(app_path, "--doctor", exec_error: Errno::ENOENT.new) }.to raise_error(SystemExit)
98+
expect(stderr_output.string).to include("could not execute 'node' because it was not found or is not executable")
99+
expect(stderr_output.string).to include("rake shakapacker:binstubs")
100+
end
101+
end
102+
end
103+
104+
it "aborts with upgrade guidance when a legacy JavaScript binstub executable is not executable" do
105+
Dir.mktmpdir("shakapacker-export-bundler-config-") do |app_path|
106+
write_app_binstub(app_path, <<~JS)
107+
#!/usr/bin/env node
108+
console.log("legacy binstub")
109+
JS
110+
111+
capture_stderr do |stderr_output|
112+
expect { invoke_task(app_path, "--doctor", exec_error: Errno::EACCES.new) }.to raise_error(SystemExit)
113+
expect(stderr_output.string).to include("could not execute 'node' because it was not found or is not executable")
114+
expect(stderr_output.string).to include("rake shakapacker:binstubs")
115+
end
116+
end
117+
end
118+
119+
it "aborts with guidance when a direct-path legacy JavaScript binstub executable is missing" do
120+
Dir.mktmpdir("shakapacker-export-bundler-config-") do |app_path|
121+
write_app_binstub(app_path, <<~JS)
122+
#!/usr/local/bin/node
123+
console.log("legacy binstub")
124+
JS
125+
126+
capture_stderr do |stderr_output|
127+
expect { invoke_task(app_path, "--doctor", exec_error: Errno::ENOENT.new) }.to raise_error(SystemExit)
128+
expect(stderr_output.string).to include("could not execute '/usr/local/bin/node' because it was not found or is not executable")
129+
expect(stderr_output.string).to include("rake shakapacker:binstubs")
130+
end
53131
end
54132
end
55133

@@ -63,4 +141,27 @@ def invoke_task(app_path, *args)
63141
expect(invoke_task(app_path, "--save")).to eq([RbConfig.ruby, binstub_path, "--save"])
64142
end
65143
end
144+
145+
it "uses the gem version when bin/shakapacker-config is a directory" do
146+
Dir.mktmpdir("shakapacker-export-bundler-config-") do |app_path|
147+
FileUtils.mkdir_p(File.join(app_path, "bin", "shakapacker-config"))
148+
gem_bin_path = File.expand_path("../../lib/install/bin/shakapacker-config", __dir__)
149+
150+
expect(invoke_task(app_path)).to eq([RbConfig.ruby, gem_bin_path])
151+
end
152+
end
153+
154+
it "aborts when the Ruby binstub interpreter is missing" do
155+
Dir.mktmpdir("shakapacker-export-bundler-config-") do |app_path|
156+
write_app_binstub(app_path, <<~RUBY)
157+
#!/usr/bin/env ruby
158+
puts "ruby binstub"
159+
RUBY
160+
161+
capture_stderr do |stderr_output|
162+
expect { invoke_task(app_path, "--save", exec_error: Errno::ENOENT.new) }.to raise_error(SystemExit)
163+
expect(stderr_output.string).to include("Ruby interpreter '#{RbConfig.ruby}' was not found or is not executable")
164+
end
165+
end
166+
end
66167
end
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
require "spec_helper"
2+
require "fileutils"
3+
require "tempfile"
4+
require "shakapacker/utils/misc"
5+
6+
RSpec.describe Shakapacker::Utils::Misc do
7+
describe ".js_binstub_executable" do
8+
def with_binstub(contents)
9+
file = Tempfile.new(["binstub-", ""])
10+
file.binmode
11+
file.write(contents)
12+
file.close
13+
yield file.path
14+
ensure
15+
file&.close
16+
file&.unlink
17+
end
18+
19+
it "returns node for a node `env` shebang" do
20+
with_binstub("#!/usr/bin/env node\nconsole.log('legacy')\n") do |path|
21+
expect(described_class.js_binstub_executable(path)).to eq "node"
22+
end
23+
end
24+
25+
it "returns node for a node `env` shebang with CRLF line endings" do
26+
with_binstub("#!/usr/bin/env node\r\nconsole.log('legacy')\n") do |path|
27+
expect(described_class.js_binstub_executable(path)).to eq "node"
28+
end
29+
end
30+
31+
it "returns node for a node `env` shebang with env flags" do
32+
with_binstub("#!/usr/bin/env -S node --no-warnings\nconsole.log('legacy')\n") do |path|
33+
expect(described_class.js_binstub_executable(path)).to eq "node"
34+
end
35+
end
36+
37+
it "returns node for a node `env` shebang with env flags that take arguments" do
38+
with_binstub("#!/usr/bin/env -u NODE_PATH -C /tmp -P /opt/node/bin -a node-shim node\nconsole.log('legacy')\n") do |path|
39+
expect(described_class.js_binstub_executable(path)).to eq "node"
40+
end
41+
end
42+
43+
it "returns node for a node `env` shebang with environment assignments" do
44+
with_binstub("#!/usr/bin/env NODE_PATH=/opt/local/lib PATH=/opt/node/bin node\nconsole.log('legacy')\n") do |path|
45+
expect(described_class.js_binstub_executable(path)).to eq "node"
46+
end
47+
end
48+
49+
it "returns the executable path for a direct node shebang" do
50+
with_binstub("#!/usr/local/bin/node\nconsole.log('legacy')\n") do |path|
51+
expect(described_class.js_binstub_executable(path)).to eq "/usr/local/bin/node"
52+
end
53+
end
54+
55+
it "returns nodejs for a nodejs shebang" do
56+
with_binstub("#!/usr/bin/env nodejs\nconsole.log('legacy')\n") do |path|
57+
expect(described_class.js_binstub_executable(path)).to eq "nodejs"
58+
end
59+
end
60+
61+
it "returns the executable path for a direct nodejs shebang" do
62+
with_binstub("#!/usr/bin/nodejs\nconsole.log('legacy')\n") do |path|
63+
expect(described_class.js_binstub_executable(path)).to eq "/usr/bin/nodejs"
64+
end
65+
end
66+
67+
it "returns nil for a ruby `env` shebang" do
68+
with_binstub("#!/usr/bin/env ruby\nputs 'ruby'\n") do |path|
69+
expect(described_class.js_binstub_executable(path)).to be_nil
70+
end
71+
end
72+
73+
it "returns nil for a direct ruby shebang" do
74+
with_binstub("#!/usr/bin/ruby\nputs 'ruby'\n") do |path|
75+
expect(described_class.js_binstub_executable(path)).to be_nil
76+
end
77+
end
78+
79+
it "returns nil when node appears after the shebang interpreter" do
80+
with_binstub("#!/usr/bin/ruby node\nputs 'ruby'\n") do |path|
81+
expect(described_class.js_binstub_executable(path)).to be_nil
82+
end
83+
end
84+
85+
it "returns nil for shebangs that only contain node as a substring" do
86+
with_binstub("#!/usr/bin/env nodemon\nconsole.log('legacy')\n") do |path|
87+
expect(described_class.js_binstub_executable(path)).to be_nil
88+
end
89+
end
90+
91+
it "returns nil when the file has no shebang" do
92+
with_binstub("just text\n") do |path|
93+
expect(described_class.js_binstub_executable(path)).to be_nil
94+
end
95+
end
96+
97+
it "returns nil for malformed shebangs" do
98+
with_binstub("#!/usr/bin/env 'node\nconsole.log('legacy')\n") do |path|
99+
expect(described_class.js_binstub_executable(path)).to be_nil
100+
end
101+
end
102+
103+
it "returns nil for an empty file" do
104+
with_binstub("") do |path|
105+
expect(described_class.js_binstub_executable(path)).to be_nil
106+
end
107+
end
108+
109+
it "returns nil when the path does not exist" do
110+
expect(
111+
described_class.js_binstub_executable("/no/such/path/anywhere")
112+
).to be_nil
113+
end
114+
115+
it "returns nil when the path is a directory" do
116+
Dir.mktmpdir do |dir|
117+
expect(described_class.js_binstub_executable(dir)).to be_nil
118+
end
119+
end
120+
end
121+
end

0 commit comments

Comments
 (0)