Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Version Checker's hard-coded path for package.json #1657

Merged
merged 8 commits into from
Dec 8, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ Please follow the recommendations outlined at [keepachangelog.com](http://keepac
### [Unreleased]
Changes since the last non-beta release.

#### Added(https://github.com/AbanoubGhadban).
#### Fixed

- Changed the ReactOnRails' version checker to check package.jsons in both root and client directories. [PR 1657](https://github.com/shakacode/react_on_rails/pull/1657) by [judahmeek](https://github.com/judahmeek).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


#### Added
- Added streaming server rendering support:
- [PR #1633](https://github.com/shakacode/react_on_rails/pull/1633) by [AbanoubGhadban](https://github.com/AbanoubGhadban).
- New `stream_react_component` helper for adding streamed components to views
Expand Down
5 changes: 3 additions & 2 deletions lib/react_on_rails/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
module ReactOnRails
class Engine < ::Rails::Engine
config.to_prepare do
if File.exist?(VersionChecker::NodePackageVersion.package_json_path)
VersionChecker.build.raise_if_gem_and_node_package_versions_differ
if VersionChecker.instance("package.json").raise_if_gem_and_node_package_versions_differ &&
VersionChecker.instance("client/package.json").raise_if_gem_and_node_package_versions_differ
Rails.logger.warn("No 'react-on-rails' entry found in 'dependencies' in package.json or client/package.json.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Isn't this a breaking change?
  2. Semantically, having methods with side effect of raise does not seem right for an if

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all this consistency checking logic should go to one method, that logs warnings for now, and will throw for the next major version bump

end
ReactOnRails::ServerRenderingPool.reset_pool
end
Expand Down
29 changes: 18 additions & 11 deletions lib/react_on_rails/version_checker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ def self.build
new(NodePackageVersion.build)
end

def self.instance(package_json_path)
justin808 marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need the arg?

can we just use node_modules_location ?

new(NodePackageVersion.new(package_json_path))
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider adding parameter validation for package_json_path

The new instance method accepts a path parameter without validation. This could lead to issues if invalid paths are provided.

 def self.instance(package_json_path)
+  raise ReactOnRails::Error, "package_json_path cannot be nil" if package_json_path.nil?
+  raise ReactOnRails::Error, "package_json_path must be a string" unless package_json_path.is_a?(String)
   new(NodePackageVersion.new(package_json_path))
 end
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def self.instance(package_json_path)
new(NodePackageVersion.new(package_json_path))
end
def self.instance(package_json_path)
raise ReactOnRails::Error, "package_json_path cannot be nil" if package_json_path.nil?
raise ReactOnRails::Error, "package_json_path must be a string" unless package_json_path.is_a?(String)
new(NodePackageVersion.new(package_json_path))
end


def initialize(node_package_version)
@node_package_version = node_package_version
end
Expand All @@ -20,17 +24,19 @@ def initialize(node_package_version)
# unless the user really knows what they're doing. So we will give a
# warning if they do not.
def raise_if_gem_and_node_package_versions_differ
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is anybody code still calling this one?

we'll need a reminder for the next big update? Maybe leave a PR pending?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can create a follow-up PR and leave it pending.

return true unless node_package_version.raw
return if node_package_version.relative_path?

raise_node_semver_version_warning if node_package_version.semver_wildcard?

node_major_minor_patch = node_package_version.major_minor_patch
gem_major_minor_patch = gem_major_minor_patch_version
versions_match = node_major_minor_patch[0] == gem_major_minor_patch[0] &&
node_major_minor_patch[1] == gem_major_minor_patch[1] &&
node_major_minor_patch[2] == gem_major_minor_patch[2]

raise_differing_versions_warning unless versions_match

raise_node_semver_version_warning if node_package_version.semver_wildcard?
false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider renaming method to better reflect its behavior

The method name raise_if_gem_and_node_package_versions_differ suggests it will raise an exception, but it now logs warnings and returns a boolean. Consider renaming to something like check_gem_and_node_package_versions or verify_version_match to better reflect its current behavior.

Additionally, the false return value on line 35 seems arbitrary and its purpose is unclear.

-def raise_if_gem_and_node_package_versions_differ
+def check_gem_and_node_package_versions
   return true unless node_package_version.raw
   return if node_package_version.relative_path?

   raise_node_semver_version_warning if node_package_version.semver_wildcard?

   node_major_minor_patch = node_package_version.major_minor_patch
   gem_major_minor_patch = gem_major_minor_patch_version
   versions_match = node_major_minor_patch[0] == gem_major_minor_patch[0] &&
                    node_major_minor_patch[1] == gem_major_minor_patch[1] &&
                    node_major_minor_patch[2] == gem_major_minor_patch[2]

   raise_differing_versions_warning unless versions_match
-  false
+  versions_match
 end
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return true unless node_package_version.raw
return if node_package_version.relative_path?
raise_node_semver_version_warning if node_package_version.semver_wildcard?
node_major_minor_patch = node_package_version.major_minor_patch
gem_major_minor_patch = gem_major_minor_patch_version
versions_match = node_major_minor_patch[0] == gem_major_minor_patch[0] &&
node_major_minor_patch[1] == gem_major_minor_patch[1] &&
node_major_minor_patch[2] == gem_major_minor_patch[2]
raise_differing_versions_warning unless versions_match
raise_node_semver_version_warning if node_package_version.semver_wildcard?
false
def check_gem_and_node_package_versions
return true unless node_package_version.raw
return if node_package_version.relative_path?
raise_node_semver_version_warning if node_package_version.semver_wildcard?
node_major_minor_patch = node_package_version.major_minor_patch
gem_major_minor_patch = gem_major_minor_patch_version
versions_match = node_major_minor_patch[0] == gem_major_minor_patch[0] &&
node_major_minor_patch[1] == gem_major_minor_patch[1] &&
node_major_minor_patch[2] == gem_major_minor_patch[2]
raise_differing_versions_warning unless versions_match
versions_match
end

end

private
Expand All @@ -43,18 +49,19 @@ def common_error_msg
your installed node package. Do not use >= or ~> in your Gemfile for react_on_rails.
Do not use ^ or ~ in your package.json for react-on-rails.
Run `yarn add react-on-rails --exact` in the directory containing folder node_modules.
***This warning will become a fatal error in ReactOnRails v15***
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Review warning message implementation

The current implementation:

  1. Always displays verbose warning messages
  2. Includes a note about fatal errors in v15, which might cause unnecessary concern
  3. Logs warnings for both version mismatches and semver wildcards

Consider:

  1. Making warning messages configurable or less verbose
  2. Removing the v15 warning if it's not immediately relevant
  3. Adding a debug log level for detailed information

Also applies to: 53-54, 58-60

MSG
end

def raise_differing_versions_warning
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

method names can't be raise, but mean log

msg = "**ERROR** ReactOnRails: ReactOnRails gem and node package versions do not match\n#{common_error_msg}"
raise ReactOnRails::Error, msg
msg = "**WARNING** ReactOnRails: ReactOnRails gem and node package versions do not match\n#{common_error_msg}"
Rails.logger.warn(msg)
end

def raise_node_semver_version_warning
msg = "**ERROR** ReactOnRails: Your node package version for react-on-rails contains a " \
msg = "**WARNING** ReactOnRails: Your node package version for react-on-rails contains a " \
"^ or ~\n#{common_error_msg}"
raise ReactOnRails::Error, msg
Rails.logger.warn(msg)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Simplify warning messages per feedback

The current warning messages are still verbose and the v15 warning may cause unnecessary concern. Per justin808's feedback, these should be more concise.

 def common_error_msg
   <<-MSG.strip_heredoc
      Detected: #{node_package_version.raw}
           gem: #{gem_version}
-     Ensure the installed version of the gem is the same as the version of
-     your installed node package. Do not use >= or ~> in your Gemfile for react_on_rails.
-     Do not use ^ or ~ in your package.json for react-on-rails.
-     Run `yarn add react-on-rails --exact` in the directory containing folder node_modules.
-     ***This warning will become a fatal error in ReactOnRails v15***
+     To fix: Run `yarn add react-on-rails@#{gem_version} --exact`
   MSG
 end

 def raise_differing_versions_warning
-  msg = "**WARNING** ReactOnRails: ReactOnRails gem and node package versions do not match\n#{common_error_msg}"
+  msg = "Version mismatch between gem and node package\n#{common_error_msg}"
   Rails.logger.warn(msg)
 end

 def raise_node_semver_version_warning
-  msg = "**WARNING** ReactOnRails: Your node package version for react-on-rails contains a " \
-        "^ or ~\n#{common_error_msg}"
+  msg = "Semver wildcards not supported in package.json\n#{common_error_msg}"
   Rails.logger.warn(msg)
 end
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
***This warning will become a fatal error in ReactOnRails v15***
MSG
end
def raise_differing_versions_warning
msg = "**ERROR** ReactOnRails: ReactOnRails gem and node package versions do not match\n#{common_error_msg}"
raise ReactOnRails::Error, msg
msg = "**WARNING** ReactOnRails: ReactOnRails gem and node package versions do not match\n#{common_error_msg}"
Rails.logger.warn(msg)
end
def raise_node_semver_version_warning
msg = "**ERROR** ReactOnRails: Your node package version for react-on-rails contains a " \
msg = "**WARNING** ReactOnRails: Your node package version for react-on-rails contains a " \
"^ or ~\n#{common_error_msg}"
raise ReactOnRails::Error, msg
Rails.logger.warn(msg)
def common_error_msg
<<-MSG.strip_heredoc
Detected: #{node_package_version.raw}
gem: #{gem_version}
To fix: Run `yarn add react-on-rails@#{gem_version} --exact`
MSG
end
def raise_differing_versions_warning
msg = "Version mismatch between gem and node package\n#{common_error_msg}"
Rails.logger.warn(msg)
end
def raise_node_semver_version_warning
msg = "Semver wildcards not supported in package.json\n#{common_error_msg}"
Rails.logger.warn(msg)
end

end

def gem_version
Expand All @@ -73,21 +80,21 @@ def self.build
new(package_json_path)
end

def self.package_json_path
Rails.root.join("client", "package.json")
def self.package_json_path(relative_path = "package.json")
Rails.root.join(relative_path)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add path validation for package.json

The method accepts any relative path without validation. Consider adding checks to:

  1. Ensure the path ends with 'package.json'
  2. Validate that the path doesn't escape the Rails root directory
 def self.package_json_path(relative_path = "package.json")
+  unless relative_path.end_with?("package.json")
+    raise ReactOnRails::Error, "Path must end with package.json"
+  end
+  path = Rails.root.join(relative_path)
+  unless path.to_s.start_with?(Rails.root.to_s)
+    raise ReactOnRails::Error, "Path must be within Rails root directory"
+  end
-  Rails.root.join(relative_path)
+  path
 end
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def self.package_json_path(relative_path = "package.json")
Rails.root.join(relative_path)
def self.package_json_path(relative_path = "package.json")
unless relative_path.end_with?("package.json")
raise ReactOnRails::Error, "Path must end with package.json"
end
path = Rails.root.join(relative_path)
unless path.to_s.start_with?(Rails.root.to_s)
raise ReactOnRails::Error, "Path must be within Rails root directory"
end
path

end

def initialize(package_json)
@package_json = package_json
end

def raw
return nil unless File.exist?(package_json)

parsed_package_contents = JSON.parse(package_json_contents)
if parsed_package_contents.key?("dependencies") &&
parsed_package_contents["dependencies"].key?("react-on-rails")
parsed_package_contents["dependencies"]["react-on-rails"]
else
raise ReactOnRails::Error, "No 'react-on-rails' entry in package.json dependencies"
end
end

Expand All @@ -96,7 +103,7 @@ def semver_wildcard?
end

def relative_path?
raw.match(%r{(\.\.|\Afile:///)}).present?
raw.match(/(\.\.|\Afile:)/).present?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Security: Strengthen relative path detection

The current regex allows potentially dangerous path patterns:

  1. .. could allow directory traversal
  2. file: protocol could allow access to arbitrary files

Consider using a more restrictive approach:

-  raw.match(/(\.\.|\Afile:)/).present?
+  raw.start_with?('file:./') || raw.start_with?('file:src/')

Committable suggestion skipped: line range outside the PR's diff.

end

def major_minor_patch
Expand Down
10 changes: 10 additions & 0 deletions spec/react_on_rails/fixtures/yalc_package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"dependencies": {
"babel": "^6.3.26",
"react-on-rails": "file:.yalc/react-on-rails",
"webpack": "^1.12.8"
},
"devDependencies": {
"babel-eslint": "^5.0.0-beta6"
}
}
52 changes: 40 additions & 12 deletions spec/react_on_rails/version_checker_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,12 @@ module ReactOnRails

before { stub_gem_version("2.2.5") }

it "does raise" do
error = /ReactOnRails: Your node package version for react-on-rails contains a \^ or ~/
expect { check_version(node_package_version) }.to raise_error(error)
it "logs" do
allow(Rails.logger).to receive(:warn)
message = /ReactOnRails: Your node package version for react-on-rails contains a \^ or ~/
# expect { check_version(node_package_version) }.to raise_error(message)
check_version(node_package_version)
expect(Rails.logger).to have_received(:warn).with(message)
end
end

Expand All @@ -48,9 +51,12 @@ module ReactOnRails

before { stub_gem_version("12.0.0.beta.1") }

it "raises" do
error = /ReactOnRails: ReactOnRails gem and node package versions do not match/
expect { check_version(node_package_version) }.to raise_error(error)
it "logs" do
allow(Rails.logger).to receive(:warn)
message = /ReactOnRails: ReactOnRails gem and node package versions do not match/
# expect { check_version(node_package_version) }.to raise_error(message)
check_version(node_package_version)
expect(Rails.logger).to have_received(:warn).with(message)
end
end

Expand All @@ -61,9 +67,12 @@ module ReactOnRails

before { stub_gem_version("13.1.0") }

it "raises" do
error = /ReactOnRails: ReactOnRails gem and node package versions do not match/
expect { check_version(node_package_version) }.to raise_error(error)
it "logs" do
allow(Rails.logger).to receive(:warn)
message = /ReactOnRails: ReactOnRails gem and node package versions do not match/
# expect { check_version(node_package_version) }.to raise_error(message)
check_version(node_package_version)
expect(Rails.logger).to have_received(:warn).with(message)
end
end

Expand All @@ -74,9 +83,12 @@ module ReactOnRails

before { stub_gem_version("13.0.0") }

it "raises" do
error = /ReactOnRails: ReactOnRails gem and node package versions do not match/
expect { check_version(node_package_version) }.to raise_error(error)
it "logs" do
allow(Rails.logger).to receive(:warn)
message = /ReactOnRails: ReactOnRails gem and node package versions do not match/
# expect { check_version(node_package_version) }.to raise_error(message)
check_version(node_package_version)
expect(Rails.logger).to have_received(:warn).with(message)
end
end

Expand Down Expand Up @@ -193,6 +205,22 @@ def check_version(node_package_version)
specify { expect(node_package_version.major_minor_patch).to be_nil }
end
end

context "with node version of 'file:.yalc/react-on-rails'" do
let(:package_json) { File.expand_path("fixtures/yalc_package.json", __dir__) }

describe "#raw" do
specify { expect(node_package_version.raw).to eq("file:.yalc/react-on-rails") }
end

describe "#relative_path?" do
specify { expect(node_package_version.relative_path?).to be true }
end

describe "#major" do
specify { expect(node_package_version.major_minor_patch).to be_nil }
end
end
end
end
end
Loading