Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ After a release, please make sure to run `bundle exec rake update_changelog`. Th

Changes since the last non-beta release.

#### Fixed

- Fixed invalid warnings about non-exact versions when using a pre-release version of React on Rails, as well as missing warnings when using different pre-release versions of the gem and the Node package. [PR 1742](https://github.com/shakacode/react_on_rails/pull/1742) by [alexeyr-ci2](https://github.com/alexeyr-ci2).

### [15.0.0-rc.1] - 2025-06-18

#### Improved
Expand Down
2 changes: 1 addition & 1 deletion lib/react_on_rails/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ def validate_generated_component_packs_loading_strategy
1. Use :sync or :defer loading strategy instead of :async
2. Upgrade to Shakapacker v8.2.0 or above to enable async script loading
MSG
if PackerUtils.shakapacker_version_requirement_met?([8, 2, 0])
if PackerUtils.shakapacker_version_requirement_met?("8.2.0")
self.generated_component_packs_loading_strategy ||= :async
elsif generated_component_packs_loading_strategy.nil?
Rails.logger.warn("**WARNING** #{msg}")
Expand Down
23 changes: 10 additions & 13 deletions lib/react_on_rails/packer_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def self.using_shakapacker_const?
return @using_shakapacker_const if defined?(@using_shakapacker_const)

@using_shakapacker_const = ReactOnRails::Utils.gem_available?("shakapacker") &&
shakapacker_version_requirement_met?([7, 0, 0])
shakapacker_version_requirement_met?("7.0.0")
end

def self.using_webpacker_const?
Expand Down Expand Up @@ -57,15 +57,16 @@ def self.shakapacker_version
end

def self.shakapacker_version_as_array
match = shakapacker_version.match(ReactOnRails::VersionChecker::MAJOR_MINOR_PATCH_VERSION_REGEX)
return @shakapacker_version_as_array if defined?(@shakapacker_version_as_array)

@shakapacker_version_as_array = [match[1].to_i, match[2].to_i, match[3].to_i]
match = shakapacker_version.match(ReactOnRails::VersionChecker::VERSION_PARTS_REGEX)

# match[4] is the pre-release version, not normally a number but something like "beta.1" or `nil`
@shakapacker_version_as_array = [match[1].to_i, match[2].to_i, match[3].to_i, match[4]].compact
end

def self.shakapacker_version_requirement_met?(required_version)
req_ver = semver_to_string(required_version)

Gem::Version.new(shakapacker_version) >= Gem::Version.new(req_ver)
Gem::Version.new(shakapacker_version) >= Gem::Version.new(required_version)
end

# This returns either a URL for the webpack-dev-server, non-server bundle or
Expand Down Expand Up @@ -171,9 +172,9 @@ def self.raise_nested_entries_disabled

def self.raise_shakapacker_version_incompatible_for_autobundling
msg = <<~MSG
**ERROR** ReactOnRails: Please upgrade Shakapacker to version #{semver_to_string(ReactOnRails::PacksGenerator::MINIMUM_SHAKAPACKER_VERSION)} or \
**ERROR** ReactOnRails: Please upgrade Shakapacker to version #{ReactOnRails::PacksGenerator::MINIMUM_SHAKAPACKER_VERSION} or \
above to use the automated bundle generation feature. The currently installed version is \
#{semver_to_string(ReactOnRails::PackerUtils.shakapacker_version_as_array)}.
#{ReactOnRails::PackerUtils.shakapacker_version}.
MSG

raise ReactOnRails::Error, msg
Expand All @@ -182,15 +183,11 @@ def self.raise_shakapacker_version_incompatible_for_autobundling
def self.raise_shakapacker_not_installed
msg = <<~MSG
**ERROR** ReactOnRails: Missing Shakapacker gem. Please upgrade to use Shakapacker \
#{semver_to_string(ReactOnRails::PacksGenerator::MINIMUM_SHAKAPACKER_VERSION)} or above to use the \
#{ReactOnRails::PacksGenerator::MINIMUM_SHAKAPACKER_VERSION} or above to use the \
automated bundle generation feature.
MSG

raise ReactOnRails::Error, msg
end

def self.semver_to_string(ary)
"#{ary[0]}.#{ary[1]}.#{ary[2]}"
end
end
end
2 changes: 1 addition & 1 deletion lib/react_on_rails/packs_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module ReactOnRails
# rubocop:disable Metrics/ClassLength
class PacksGenerator
CONTAINS_CLIENT_OR_SERVER_REGEX = /\.(server|client)($|\.)/
MINIMUM_SHAKAPACKER_VERSION = [6, 5, 1].freeze
MINIMUM_SHAKAPACKER_VERSION = "6.5.1"

def self.instance
@instance ||= PacksGenerator.new
Expand Down
34 changes: 14 additions & 20 deletions lib/react_on_rails/version_checker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ module ReactOnRails
class VersionChecker
attr_reader :node_package_version

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

def self.build
new(NodePackageVersion.build)
Expand All @@ -23,13 +24,7 @@ def log_if_gem_and_node_package_versions_differ
return if node_package_version.raw.nil? || node_package_version.local_path_or_url?
return log_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]

log_differing_versions_warning unless versions_match
log_differing_versions_warning unless node_package_version.parts == gem_version_parts
end

private
Expand All @@ -39,30 +34,29 @@ def common_error_msg
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.
your installed Node package. Do not use >= or ~> in your Gemfile for react_on_rails.
Do not use ^, ~, or other non-exact versions in your package.json for react-on-rails.
Run `yarn add react-on-rails --exact` in the directory containing folder node_modules.
MSG
end

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

def log_node_semver_version_warning
msg = "**WARNING** ReactOnRails: Your node package version for react-on-rails contains a " \
"^ or ~\n#{common_error_msg}"
msg = "**WARNING** ReactOnRails: Your Node package version for react-on-rails is not an exact version\n" \
"#{common_error_msg}"
Rails.logger.warn(msg)
end

def gem_version
ReactOnRails::VERSION
end

def gem_major_minor_patch_version
match = gem_version.match(MAJOR_MINOR_PATCH_VERSION_REGEX)
[match[1], match[2], match[3]]
def gem_version_parts
gem_version.match(VERSION_PARTS_REGEX)&.captures&.compact
end

class NodePackageVersion
Expand Down Expand Up @@ -100,7 +94,7 @@ def semver_wildcard?
# See https://docs.npmjs.com/cli/v10/configuring-npm/package-json#dependencies
# We want to disallow all expressions other than exact versions
# and the ones allowed by local_path_or_url?
raw.blank? || raw.match(/[~^><|*-]/).present?
raw.blank? || raw.start_with?(/[~^><*]/) || raw.include?(" - ") || raw.include?(" || ")
end

def local_path_or_url?
Expand All @@ -110,15 +104,15 @@ def local_path_or_url?
!raw.nil? && raw.include?("/") && !raw.start_with?("npm:")
end

def major_minor_patch
def parts
return if local_path_or_url?

match = raw.match(MAJOR_MINOR_PATCH_VERSION_REGEX)
match = raw.match(VERSION_PARTS_REGEX)
unless match
raise ReactOnRails::Error, "Cannot parse version number '#{raw}' (only exact versions are supported)"
end

[match[1], match[2], match[3]]
match.captures.compact
end

private
Expand Down
4 changes: 2 additions & 2 deletions spec/react_on_rails/configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ module ReactOnRails
context "when using Shakapacker >= 8.2.0" do
before do
allow(ReactOnRails::PackerUtils).to receive(:shakapacker_version_requirement_met?)
.with([8, 2, 0]).and_return(true)
.with("8.2.0").and_return(true)
end

it "defaults to :async" do
Expand Down Expand Up @@ -426,7 +426,7 @@ module ReactOnRails
context "when using Shakapacker < 8.2.0" do
before do
allow(ReactOnRails::PackerUtils).to receive(:shakapacker_version_requirement_met?)
.with([8, 2, 0]).and_return(false)
.with("8.2.0").and_return(false)
allow(Rails.logger).to receive(:warn)
end

Expand Down
4 changes: 2 additions & 2 deletions spec/react_on_rails/packer_utils_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
module ReactOnRails
describe PackerUtils do
describe ".shakapacker_version_requirement_met?" do
minimum_version = [6, 5, 3]
minimum_version = "6.5.3"

it "returns false when version is lower than minimum_version" do
allow(described_class).to receive(:shakapacker_version).and_return("6.5.0")
Expand All @@ -31,7 +31,7 @@ module ReactOnRails
allow(described_class).to receive(:shakapacker_version).and_return("6.5.4")
expect(described_class.shakapacker_version_requirement_met?(minimum_version)).to be(true)

allow(described_class).to receive(:shakapacker_version).and_return("7.7.7")
allow(described_class).to receive(:shakapacker_version).and_return("7.0.0")
expect(described_class.shakapacker_version_requirement_met?(minimum_version)).to be(true)
end
end
Expand Down
48 changes: 24 additions & 24 deletions spec/react_on_rails/version_checker_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ module ReactOnRails # rubocop:disable Metrics/ModuleLength

context "when gem and node package major and minor versions are equal" do
let(:node_package_version) do
double_package_version(raw: "2.2.5-beta.2", major_minor_patch: %w[2 2 5])
double_package_version(raw: "2.2.5-beta.2", parts: %w[2 2 5 beta.2])
end

before { stub_gem_version("2.2.5.beta.2") }
Expand All @@ -32,67 +32,67 @@ module ReactOnRails # rubocop:disable Metrics/ModuleLength

context "when major and minor versions are equal BUT node uses semver wildcard" do
let(:node_package_version) do
double_package_version(raw: "^2.2.5", semver_wildcard: true, major_minor_patch: %w[2 2 5])
double_package_version(raw: "^2.2.5", semver_wildcard: true, parts: %w[2 2 5])
end

before { stub_gem_version("2.2.5") }

it "logs" do
allow(Rails.logger).to receive(:warn)
message = /ReactOnRails: Your node package version for react-on-rails contains a \^ or ~/
message = /ReactOnRails: Your Node package version for react-on-rails is not an exact version/
check_version_and_log(node_package_version)
expect(Rails.logger).to have_received(:warn).with(message)
end
end

context "when gem and node package major versions differ" do
let(:node_package_version) do
double_package_version(raw: "13.0.0.beta-2", major_minor_patch: %w[13 0 0])
double_package_version(raw: "13.0.0.beta-2", parts: %w[13 0 0 beta-2])
end

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

it "logs" do
allow(Rails.logger).to receive(:warn)
message = /ReactOnRails: ReactOnRails gem and node package versions do not match/
message = /ReactOnRails: ReactOnRails gem and Node package versions do not match/
check_version_and_log(node_package_version)
expect(Rails.logger).to have_received(:warn).with(message)
end
end

context "when gem and node package major versions match and minor differs" do
let(:node_package_version) do
double_package_version(raw: "13.0.0.beta-2", major_minor_patch: %w[13 0 0])
double_package_version(raw: "13.0.0.beta-2", parts: %w[13 0 0 beta-2])
end

before { stub_gem_version("13.1.0") }

it "logs" do
allow(Rails.logger).to receive(:warn)
message = /ReactOnRails: ReactOnRails gem and node package versions do not match/
message = /ReactOnRails: ReactOnRails gem and Node package versions do not match/
check_version_and_log(node_package_version)
expect(Rails.logger).to have_received(:warn).with(message)
end
end

context "when gem and node package major, minor versions match and patch differs" do
let(:node_package_version) do
double_package_version(raw: "13.0.1", major_minor_patch: %w[13 0 1])
double_package_version(raw: "13.0.1", parts: %w[13 0 1])
end

before { stub_gem_version("13.0.0") }

it "logs" do
allow(Rails.logger).to receive(:warn)
message = /ReactOnRails: ReactOnRails gem and node package versions do not match/
message = /ReactOnRails: ReactOnRails gem and Node package versions do not match/
check_version_and_log(node_package_version)
expect(Rails.logger).to have_received(:warn).with(message)
end
end

context "when package json uses a relative path with dots" do
let(:node_package_version) do
double_package_version(raw: "../../..", major_minor_patch: "", local_path_or_url: true)
double_package_version(raw: "../../..", parts: nil, local_path_or_url: true)
end

before { stub_gem_version("2.0.0.beta.1") }
Expand All @@ -116,11 +116,11 @@ module ReactOnRails # rubocop:disable Metrics/ModuleLength
end

def double_package_version(raw: nil, semver_wildcard: false,
major_minor_patch: nil, local_path_or_url: false)
parts: nil, local_path_or_url: false)
instance_double(VersionChecker::NodePackageVersion,
raw: raw,
semver_wildcard?: semver_wildcard,
major_minor_patch: major_minor_patch,
parts: parts,
local_path_or_url?: local_path_or_url)
end

Expand Down Expand Up @@ -187,8 +187,8 @@ def check_version_and_log(node_package_version)
specify { expect(node_package_version.local_path_or_url?).to be false }
end

describe "#major" do
specify { expect(node_package_version.major_minor_patch).to eq(%w[0 0 2]) }
describe "#parts" do
specify { expect(node_package_version.parts).to eq(%w[0 0 2]) }
end
end

Expand All @@ -203,8 +203,8 @@ def check_version_and_log(node_package_version)
specify { expect(node_package_version.local_path_or_url?).to be false }
end

describe "#major_minor_patch" do
specify { expect(node_package_version.major_minor_patch).to eq(%w[14 0 0]) }
describe "#parts" do
specify { expect(node_package_version.parts).to eq(%w[14 0 0 beta-2]) }
end
end

Expand All @@ -219,8 +219,8 @@ def check_version_and_log(node_package_version)
specify { expect(node_package_version.local_path_or_url?).to be true }
end

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

Expand All @@ -235,8 +235,8 @@ def check_version_and_log(node_package_version)
specify { expect(node_package_version.local_path_or_url?).to be true }
end

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

Expand All @@ -251,8 +251,8 @@ def check_version_and_log(node_package_version)
specify { expect(node_package_version.local_path_or_url?).to be true }
end

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

Expand All @@ -267,8 +267,8 @@ def check_version_and_log(node_package_version)
specify { expect(node_package_version.local_path_or_url?).to be true }
end

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

Expand Down
Loading