@@ -17,38 +17,142 @@ def initialize(node_package_version)
1717 @node_package_version = node_package_version
1818 end
1919
20- # For compatibility, the gem and the node package versions should always match,
21- # unless the user really knows what they're doing. So we will give a
22- # warning if they do not.
23- 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?
26-
27- log_differing_versions_warning unless node_package_version . parts == gem_version_parts
20+ # Validates version and package compatibility.
21+ # Raises ReactOnRails::Error if:
22+ # - package.json file is not found
23+ # - Both react-on-rails and react-on-rails-pro packages are installed
24+ # - Pro gem is installed but using react-on-rails package
25+ # - Pro package is installed but Pro gem is not installed
26+ # - Non-exact version is used
27+ # - Versions don't match
28+ def validate_version_and_package_compatibility!
29+ validate_package_json_exists!
30+ validate_package_gem_compatibility!
31+ validate_exact_version!
32+ validate_version_match!
2833 end
2934
3035 private
3136
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.
37+ def validate_package_json_exists!
38+ return if File . exist? ( node_package_version . package_json )
39+
40+ raise ReactOnRails ::Error , <<~MSG . strip
41+ **ERROR** ReactOnRails: package.json file not found.
42+
43+ Expected location: #{ node_package_version . package_json }
44+
45+ React on Rails requires a package.json file with either 'react-on-rails' or
46+ 'react-on-rails-pro' package installed.
47+
48+ Fix:
49+ 1. Ensure you have a package.json in your project root
50+ 2. Run: yarn add react-on-rails@#{ gem_version } --exact
51+
52+ Or if using React on Rails Pro:
53+ Run: yarn add react-on-rails-pro@#{ gem_version } --exact
4054 MSG
4155 end
4256
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 )
57+ def validate_package_gem_compatibility!
58+ has_base_package = node_package_version . react_on_rails_package?
59+ has_pro_package = node_package_version . react_on_rails_pro_package?
60+ is_pro_gem = ReactOnRails ::Utils . react_on_rails_pro?
61+
62+ # Error: Both packages installed
63+ if has_base_package && has_pro_package
64+ raise ReactOnRails ::Error , <<~MSG . strip
65+ **ERROR** ReactOnRails: Both 'react-on-rails' and 'react-on-rails-pro' packages are installed.
66+
67+ If you're using React on Rails Pro, only install the 'react-on-rails-pro' package.
68+ The Pro package already includes all functionality from the base package.
69+
70+ Fix:
71+ 1. Remove 'react-on-rails' from your package.json dependencies
72+ 2. Run: yarn remove react-on-rails
73+ 3. Keep only: react-on-rails-pro
74+
75+ #{ package_json_location }
76+ MSG
77+ end
78+
79+ # Error: Pro gem but using base package
80+ if is_pro_gem && !has_pro_package
81+ raise ReactOnRails ::Error , <<~MSG . strip
82+ **ERROR** ReactOnRails: You have the Pro gem installed but are using the base 'react-on-rails' package.
83+
84+ When using React on Rails Pro, you must use the 'react-on-rails-pro' npm package.
85+
86+ Fix:
87+ 1. Remove the base package: yarn remove react-on-rails
88+ 2. Install the Pro package: yarn add react-on-rails-pro@#{ gem_version } --exact
89+
90+ #{ package_json_location }
91+ MSG
92+ end
93+
94+ # Error: Pro package but not Pro gem
95+ return unless !is_pro_gem && has_pro_package
96+
97+ raise ReactOnRails ::Error , <<~MSG . strip
98+ **ERROR** ReactOnRails: You have the 'react-on-rails-pro' package installed but the Pro gem is not installed.
99+
100+ The Pro npm package requires the Pro gem to function.
101+
102+ Fix:
103+ 1. Install the Pro gem by adding to your Gemfile:
104+ gem 'react_on_rails_pro'
105+ 2. Run: bundle install
106+
107+ Or if you meant to use the base version:
108+ 1. Remove the Pro package: yarn remove react-on-rails-pro
109+ 2. Install the base package: yarn add react-on-rails@#{ gem_version } --exact
110+
111+ #{ package_json_location }
112+ MSG
113+ end
114+
115+ def validate_exact_version!
116+ return if node_package_version . raw . nil? || node_package_version . local_path_or_url?
117+
118+ return unless node_package_version . semver_wildcard?
119+
120+ package_name = node_package_version . package_name
121+ raise ReactOnRails ::Error , <<~MSG . strip
122+ **ERROR** ReactOnRails: The '#{ package_name } ' package version is not an exact version.
123+
124+ Detected: #{ node_package_version . raw }
125+ Gem: #{ gem_version }
126+
127+ React on Rails requires exact version matching between the gem and npm package.
128+ Do not use ^, ~, >, <, *, or other semver ranges.
129+
130+ Fix:
131+ Run: yarn add #{ package_name } @#{ gem_version } --exact
132+
133+ #{ package_json_location }
134+ MSG
46135 end
47136
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 )
137+ def validate_version_match!
138+ return if node_package_version . raw . nil? || node_package_version . local_path_or_url?
139+
140+ return if node_package_version . parts == gem_version_parts
141+
142+ package_name = node_package_version . package_name
143+ raise ReactOnRails ::Error , <<~MSG . strip
144+ **ERROR** ReactOnRails: The '#{ package_name } ' package version does not match the gem version.
145+
146+ Package: #{ node_package_version . raw }
147+ Gem: #{ gem_version }
148+
149+ The npm package and gem versions must match exactly for compatibility.
150+
151+ Fix:
152+ Run: yarn add #{ package_name } @#{ gem_version } --exact
153+
154+ #{ package_json_location }
155+ MSG
52156 end
53157
54158 def gem_version
@@ -59,6 +163,10 @@ def gem_version_parts
59163 gem_version . match ( VERSION_PARTS_REGEX ) &.captures &.compact
60164 end
61165
166+ def package_json_location
167+ "Package.json location: #{ VersionChecker ::NodePackageVersion . package_json_path } "
168+ end
169+
62170 class NodePackageVersion
63171 attr_reader :package_json
64172
@@ -77,19 +185,41 @@ def initialize(package_json)
77185 def raw
78186 return @raw if defined? ( @raw )
79187
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
86- 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"
188+ return @raw = nil unless File . exist? ( package_json )
189+
190+ parsed = parsed_package_contents
191+ return @raw = nil unless parsed . key? ( "dependencies" )
192+
193+ deps = parsed [ "dependencies" ]
194+
195+ # Check for react-on-rails-pro first (Pro takes precedence)
196+ return @raw = deps [ "react-on-rails-pro" ] if deps . key? ( "react-on-rails-pro" )
197+
198+ # Fall back to react-on-rails
199+ return @raw = deps [ "react-on-rails" ] if deps . key? ( "react-on-rails" )
200+
201+ # Neither package found
202+ msg = "No 'react-on-rails' or 'react-on-rails-pro' entry in the dependencies of " \
203+ "#{ NodePackageVersion . package_json_path } , which is the expected location according to " \
204+ "ReactOnRails.configuration.node_modules_location"
89205 Rails . logger . warn ( msg )
90206 @raw = nil
91207 end
92208
209+ def react_on_rails_package?
210+ package_installed? ( "react-on-rails" )
211+ end
212+
213+ def react_on_rails_pro_package?
214+ package_installed? ( "react-on-rails-pro" )
215+ end
216+
217+ def package_name
218+ return "react-on-rails-pro" if react_on_rails_pro_package?
219+
220+ "react-on-rails"
221+ end
222+
93223 def semver_wildcard?
94224 # See https://docs.npmjs.com/cli/v10/configuring-npm/package-json#dependencies
95225 # We want to disallow all expressions other than exact versions
@@ -117,9 +247,20 @@ def parts
117247
118248 private
119249
250+ def package_installed? ( package_name )
251+ return false unless File . exist? ( package_json )
252+
253+ parsed = parsed_package_contents
254+ parsed . dig ( "dependencies" , package_name ) . present?
255+ end
256+
120257 def package_json_contents
121258 @package_json_contents ||= File . read ( package_json )
122259 end
260+
261+ def parsed_package_contents
262+ @parsed_package_contents ||= JSON . parse ( package_json_contents )
263+ end
123264 end
124265 end
125266end
0 commit comments