Skip to content

Commit 59b4c68

Browse files
Unify React on Rails Pro versions with main package (4.x → 16.x)
This commit implements exact version matching across all React on Rails packages and gems to ensure guaranteed compatibility: **Version Changes:** - Bump React on Rails Pro gem from 4.0.0 to 16.1.1 - Bump node renderer package from 4.0.0 to 16.1.1 - Keep PROTOCOL_VERSION independent at 2.0.0 (only changes on protocol updates) **Version Checker Enhancements:** - Check both react-on-rails and react-on-rails-pro packages - Warn if both packages are present (only Pro needed) - Enforce exact versions for all packages (no ^, ~, >=, etc.) - Check node-renderer package version against Pro gem - Raise errors in dev/test environments, log in production **Dependency Updates:** - Pro gem now requires exact version match with main gem - Changed from `>= 16.0.0` to `ReactOnRailsPro::VERSION` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 64dc33a commit 59b4c68

File tree

4 files changed

+175
-51
lines changed

4 files changed

+175
-51
lines changed

lib/react_on_rails/version_checker.rb

Lines changed: 167 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -4,63 +4,153 @@ module ReactOnRails
44
# Responsible for checking versions of rubygem versus yarn node package
55
# against each other at runtime.
66
class VersionChecker
7-
attr_reader :node_package_version
7+
attr_reader :package_json_data
88

99
# Semver uses - to separate pre-release, but RubyGems use .
1010
VERSION_PARTS_REGEX = /(\d+)\.(\d+)\.(\d+)(?:[-.]([0-9A-Za-z.-]+))?/
1111

1212
def self.build
13-
new(NodePackageVersion.build)
13+
new(PackageJsonData.build)
1414
end
1515

16-
def initialize(node_package_version)
17-
@node_package_version = node_package_version
16+
def initialize(package_json_data)
17+
@package_json_data = package_json_data
1818
end
1919

2020
# For compatibility, the gem and the node package versions should always match,
2121
# unless the user really knows what they're doing. So we will give a
2222
# warning if they do not.
2323
def log_if_gem_and_node_package_versions_differ
24-
return if node_package_version.raw.nil? || node_package_version.local_path_or_url?
25-
return log_node_semver_version_warning if node_package_version.semver_wildcard?
24+
errors = []
2625

27-
log_differing_versions_warning unless node_package_version.parts == gem_version_parts
26+
# Check if both react-on-rails and react-on-rails-pro are present
27+
check_for_both_packages(errors)
28+
29+
# Check react-on-rails or react-on-rails-pro package
30+
check_main_package_version(errors)
31+
32+
# Check node-renderer package if Pro is present
33+
check_node_renderer_version(errors) if package_json_data.pro_package?
34+
35+
# Handle errors based on environment
36+
handle_errors(errors) if errors.any?
2837
end
2938

3039
private
3140

32-
def common_error_msg
33-
<<-MSG.strip_heredoc
34-
Detected: #{node_package_version.raw}
35-
gem: #{gem_version}
36-
Ensure the installed version of the gem is the same as the version of
37-
your installed Node package. Do not use >= or ~> in your Gemfile for react_on_rails.
38-
Do not use ^, ~, or other non-exact versions in your package.json for react-on-rails.
39-
Run `yarn add react-on-rails --exact` in the directory containing folder node_modules.
41+
def check_for_both_packages(errors)
42+
return unless package_json_data.both_packages?
43+
44+
msg = <<~MSG.strip_heredoc
45+
React on Rails: Both 'react-on-rails' and 'react-on-rails-pro' packages are detected in package.json.
46+
You only need to install 'react-on-rails-pro' package as it already includes 'react-on-rails' as a dependency.
47+
Please remove 'react-on-rails' from your package.json dependencies.
48+
MSG
49+
errors << { type: :warning, message: msg }
50+
end
51+
52+
def check_main_package_version(errors)
53+
package_name = package_json_data.pro_package? ? "react-on-rails-pro" : "react-on-rails"
54+
package_version_data = package_json_data.get_package_version(package_name)
55+
56+
return if package_version_data.nil?
57+
return if package_version_data.local_path_or_url?
58+
59+
# Check for exact version (no semver wildcards)
60+
if package_version_data.semver_wildcard?
61+
msg = build_semver_wildcard_error(package_name, package_version_data.raw)
62+
errors << { type: :error, message: msg }
63+
return
64+
end
65+
66+
# Check version match
67+
expected_version = package_json_data.pro_package? ? pro_gem_version : gem_version
68+
return if package_version_data.parts == version_parts(expected_version)
69+
70+
msg = build_version_mismatch_error(package_name, package_version_data.raw, expected_version)
71+
errors << { type: :error, message: msg }
72+
end
73+
74+
def check_node_renderer_version(errors)
75+
node_renderer_data = package_json_data.get_package_version("@shakacode-tools/react-on-rails-pro-node-renderer")
76+
return if node_renderer_data.nil?
77+
return if node_renderer_data.local_path_or_url?
78+
79+
# Check for exact version
80+
if node_renderer_data.semver_wildcard?
81+
msg = build_semver_wildcard_error("@shakacode-tools/react-on-rails-pro-node-renderer",
82+
node_renderer_data.raw)
83+
errors << { type: :error, message: msg }
84+
return
85+
end
86+
87+
# Check version match with Pro gem
88+
return if node_renderer_data.parts == version_parts(pro_gem_version)
89+
90+
msg = build_version_mismatch_error("@shakacode-tools/react-on-rails-pro-node-renderer",
91+
node_renderer_data.raw, pro_gem_version)
92+
errors << { type: :error, message: msg }
93+
end
94+
95+
def build_semver_wildcard_error(package_name, raw_version)
96+
<<~MSG.strip_heredoc
97+
React on Rails: Package '#{package_name}' is using a non-exact version: #{raw_version}
98+
For guaranteed compatibility, you must use exact versions (no ^, ~, >=, etc.).
99+
Run: yarn add #{package_name}@#{expected_version_for_package(package_name)} --exact
100+
MSG
101+
end
102+
103+
def build_version_mismatch_error(package_name, package_version, gem_version)
104+
<<~MSG.strip_heredoc
105+
React on Rails: Package '#{package_name}' version does not match the gem version.
106+
Package version: #{package_version}
107+
Gem version: #{gem_version}
108+
Run: yarn add #{package_name}@#{gem_version} --exact
40109
MSG
41110
end
42111

43-
def log_differing_versions_warning
44-
msg = "**WARNING** ReactOnRails: ReactOnRails gem and Node package versions do not match\n#{common_error_msg}"
45-
Rails.logger.warn(msg)
112+
def expected_version_for_package(package_name)
113+
case package_name
114+
when "react-on-rails"
115+
gem_version
116+
when "react-on-rails-pro", "@shakacode-tools/react-on-rails-pro-node-renderer"
117+
pro_gem_version
118+
end
46119
end
47120

48-
def log_node_semver_version_warning
49-
msg = "**WARNING** ReactOnRails: Your Node package version for react-on-rails is not an exact version\n" \
50-
"#{common_error_msg}"
51-
Rails.logger.warn(msg)
121+
def handle_errors(errors)
122+
errors.each do |error|
123+
if error[:type] == :warning
124+
Rails.logger.warn("**WARNING** #{error[:message]}")
125+
elsif development_or_test?
126+
raise ReactOnRails::Error, error[:message]
127+
else
128+
Rails.logger.error("**ERROR** #{error[:message]}")
129+
end
130+
end
131+
end
132+
133+
def development_or_test?
134+
Rails.env.development? || Rails.env.test?
52135
end
53136

54137
def gem_version
55138
ReactOnRails::VERSION
56139
end
57140

58-
def gem_version_parts
59-
gem_version.match(VERSION_PARTS_REGEX)&.captures&.compact
141+
def pro_gem_version
142+
return nil unless defined?(ReactOnRailsPro)
143+
144+
ReactOnRailsPro::VERSION
60145
end
61146

62-
class NodePackageVersion
63-
attr_reader :package_json
147+
def version_parts(version)
148+
version&.match(VERSION_PARTS_REGEX)&.captures&.compact
149+
end
150+
151+
# Represents package.json data and provides methods to check for packages
152+
class PackageJsonData
153+
attr_reader :package_json_path
64154

65155
def self.build
66156
new(package_json_path)
@@ -70,24 +160,61 @@ def self.package_json_path
70160
Rails.root.join(ReactOnRails.configuration.node_modules_location, "package.json")
71161
end
72162

73-
def initialize(package_json)
74-
@package_json = package_json
163+
def initialize(package_json_path)
164+
@package_json_path = package_json_path
165+
end
166+
167+
def pro_package?
168+
package_exists?("react-on-rails-pro")
169+
end
170+
171+
def main_package?
172+
package_exists?("react-on-rails")
173+
end
174+
175+
def both_packages?
176+
main_package? && pro_package?
177+
end
178+
179+
def get_package_version(package_name)
180+
version = find_package_version(package_name)
181+
return nil if version.nil?
182+
183+
PackageVersion.new(version)
75184
end
76185

77-
def raw
78-
return @raw if defined?(@raw)
186+
private
187+
188+
def package_exists?(package_name)
189+
!find_package_version(package_name).nil?
190+
end
79191

80-
if File.exist?(package_json)
81-
parsed_package_contents = JSON.parse(package_json_contents)
82-
if parsed_package_contents.key?("dependencies") &&
83-
parsed_package_contents["dependencies"].key?("react-on-rails")
84-
return @raw = parsed_package_contents["dependencies"]["react-on-rails"]
85-
end
192+
def find_package_version(package_name)
193+
return nil unless File.exist?(package_json_path)
194+
195+
parsed = parsed_package_json
196+
return nil unless parsed
197+
198+
parsed.dig("dependencies", package_name) || parsed.dig("devDependencies", package_name)
199+
end
200+
201+
def parsed_package_json
202+
return @parsed_package_json if defined?(@parsed_package_json)
203+
204+
@parsed_package_json = begin
205+
JSON.parse(File.read(package_json_path))
206+
rescue JSON::ParserError, Errno::ENOENT
207+
nil
86208
end
87-
msg = "No 'react-on-rails' entry in the dependencies of #{NodePackageVersion.package_json_path}, " \
88-
"which is the expected location according to ReactOnRails.configuration.node_modules_location"
89-
Rails.logger.warn(msg)
90-
@raw = nil
209+
end
210+
end
211+
212+
# Represents a package version string from package.json
213+
class PackageVersion
214+
attr_reader :raw
215+
216+
def initialize(raw)
217+
@raw = raw
91218
end
92219

93220
def semver_wildcard?
@@ -114,12 +241,6 @@ def parts
114241

115242
match.captures.compact
116243
end
117-
118-
private
119-
120-
def package_json_contents
121-
@package_json_contents ||= File.read(package_json)
122-
end
123244
end
124245
end
125246
end
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# frozen_string_literal: true
22

33
module ReactOnRailsPro
4-
VERSION = "4.0.0"
4+
# Version is now synchronized with main react_on_rails gem
5+
VERSION = "16.1.1"
6+
# Protocol version remains independent - only changes when communication protocol changes
57
PROTOCOL_VERSION = "2.0.0"
68
end

react_on_rails_pro/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@shakacode-tools/react-on-rails-pro-node-renderer",
3-
"version": "4.0.0",
3+
"version": "16.1.1",
44
"protocolVersion": "2.0.0",
55
"description": "react-on-rails-pro JavaScript for react_on_rails_pro Ruby gem",
66
"exports": {

react_on_rails_pro/react_on_rails_pro.gemspec

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ Gem::Specification.new do |s|
1717
s.metadata["rubygems_mfa_required"] = "true"
1818

1919
s.files = `git ls-files -z`.split("\x0")
20-
.reject { |f|
20+
.reject do |f|
2121
f.match(
2222
%r{^(test|spec|features|tmp|node_modules|packages|coverage|Gemfile.lock|lib/tasks)/}
2323
)
24-
}
24+
end
2525
s.bindir = "exe"
2626
s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
2727
s.require_paths = ["lib"]
@@ -34,7 +34,8 @@ Gem::Specification.new do |s|
3434
s.add_runtime_dependency "httpx", "~> 1.5"
3535
s.add_runtime_dependency "jwt", "~> 2.7"
3636
s.add_runtime_dependency "rainbow"
37-
s.add_runtime_dependency "react_on_rails", ">= 16.0.0"
37+
# Pro gem requires exact version match with main gem for guaranteed compatibility
38+
s.add_runtime_dependency "react_on_rails", ReactOnRailsPro::VERSION
3839
s.add_development_dependency "bundler"
3940
s.add_development_dependency "commonmarker"
4041
s.add_development_dependency "gem-release"

0 commit comments

Comments
 (0)