Skip to content

Commit 23be9a9

Browse files
justin808claude
andauthored
Use exact option in package_json gem for installing dependencies (#1939)
## Summary Updates the `add_npm_dependencies` helper method to use the new `exact: true` option from package_json gem v0.2.0, ensuring npm packages are installed with exact versions (no ^ or ~ prefix). ## Changes - Modified `lib/generators/react_on_rails/generator_helper.rb` to pass `exact: true` when calling `pj.manager.add()` for both regular and dev dependencies ## Motivation The package_json gem v0.2.0 (released 2025-11-06) now supports an `exact` parameter in the `add` method to install packages with exact versions. This aligns with React on Rails' requirement for strict version matching between the gem and npm packages, eliminating the need for manual version specification cleanup. ## Test plan - RuboCop passes with no offenses - Generator tests verify package installation behavior - Existing functionality preserved with enhanced exact version specification 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- Reviewable:start --> - - - This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/shakacode/react_on_rails/1939) <!-- Reviewable:end --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Enhanced npm dependency resolution during project generation to enforce exact version matching for consistency and reliability. * **Tests** * Added comprehensive test coverage for dependency management and error handling workflows. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent edd0463 commit 23be9a9

File tree

2 files changed

+111
-2
lines changed

2 files changed

+111
-2
lines changed

lib/generators/react_on_rails/generator_helper.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ def add_npm_dependencies(packages, dev: false)
2626

2727
begin
2828
if dev
29-
pj.manager.add(packages, type: :dev)
29+
pj.manager.add(packages, type: :dev, exact: true)
3030
else
31-
pj.manager.add(packages)
31+
pj.manager.add(packages, exact: true)
3232
end
3333
true
3434
rescue StandardError => e
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# frozen_string_literal: true
2+
3+
require_relative "../support/generator_spec_helper"
4+
5+
RSpec.describe GeneratorHelper, type: :generator do
6+
include described_class
7+
8+
let(:destination_root) { File.expand_path("../dummy-for-generators", __dir__) }
9+
10+
describe "#add_npm_dependencies" do
11+
context "when package_json gem is available" do
12+
let(:mock_package_json) { instance_double(PackageJson) }
13+
let(:mock_manager) { instance_double("PackageJson::Manager") } # rubocop:disable RSpec/VerifiedDoubleReference
14+
15+
before do
16+
allow(self).to receive(:package_json).and_return(mock_package_json)
17+
allow(mock_package_json).to receive(:manager).and_return(mock_manager)
18+
end
19+
20+
context "when adding regular dependencies" do
21+
it "calls manager.add with exact: true" do
22+
packages = %w[react react-dom]
23+
24+
expect(mock_manager).to receive(:add).with(packages, exact: true)
25+
26+
result = add_npm_dependencies(packages)
27+
expect(result).to be true
28+
end
29+
end
30+
31+
context "when adding dev dependencies" do
32+
it "calls manager.add with type: :dev and exact: true" do
33+
packages = ["@types/react", "@types/react-dom"]
34+
35+
expect(mock_manager).to receive(:add).with(packages, type: :dev, exact: true)
36+
37+
result = add_npm_dependencies(packages, dev: true)
38+
expect(result).to be true
39+
end
40+
end
41+
42+
context "when package_json gem raises an error" do
43+
it "returns false and prints a warning" do
44+
packages = ["react"]
45+
46+
allow(mock_manager).to receive(:add).and_raise(StandardError, "Installation failed")
47+
expect { add_npm_dependencies(packages) }.to output(/Warning: Could not add packages/).to_stdout
48+
49+
result = add_npm_dependencies(packages)
50+
expect(result).to be false
51+
end
52+
end
53+
end
54+
55+
context "when package_json gem is not available" do
56+
before do
57+
allow(self).to receive(:package_json).and_return(nil)
58+
end
59+
60+
it "returns false" do
61+
packages = ["react"]
62+
63+
result = add_npm_dependencies(packages)
64+
expect(result).to be false
65+
end
66+
end
67+
end
68+
69+
describe "#package_json" do
70+
context "when PackageJson is available" do
71+
before do
72+
stub_const("PackageJson", Class.new do
73+
def self.read
74+
new
75+
end
76+
end)
77+
end
78+
79+
it "returns a PackageJson instance" do
80+
result = package_json
81+
expect(result).to be_a(PackageJson)
82+
end
83+
84+
it "memoizes the result" do
85+
first_call = package_json
86+
second_call = package_json
87+
expect(first_call).to equal(second_call)
88+
end
89+
end
90+
91+
# NOTE: Testing the LoadError path is difficult because PackageJson is already loaded
92+
# in the test environment. The StandardError path below covers the error handling logic.
93+
94+
context "when package.json file cannot be read" do
95+
before do
96+
stub_const("PackageJson", Class.new do
97+
def self.read
98+
raise StandardError, "File not found"
99+
end
100+
end)
101+
end
102+
103+
it "returns nil and prints a warning" do
104+
expect { package_json }.to output(/Warning: Could not read package\.json/).to_stdout
105+
expect(package_json).to be_nil
106+
end
107+
end
108+
end
109+
end

0 commit comments

Comments
 (0)