Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
10 changes: 7 additions & 3 deletions .github/workflows/examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ jobs:
echo "Node version: "; node -v
echo "Yarn version: "; yarn --version
echo "Bundler version: "; bundle --version
- name: run conversion script to support shakapacker v6
- name: Run conversion script for older Node compatibility
if: matrix.dependency-level == 'minimum'
run: script/convert
- name: Save root ruby gems to cache
Expand Down Expand Up @@ -172,8 +172,12 @@ jobs:
- name: Set packer version environment variable
run: |
echo "CI_DEPENDENCY_LEVEL=${{ matrix.dependency-level }}" >> $GITHUB_ENV
- name: Main CI
run: cd react_on_rails && bundle exec rake run_rspec:shakapacker_examples
- name: Main CI - Latest version examples
if: matrix.dependency-level == 'latest'
run: cd react_on_rails && bundle exec rake run_rspec:shakapacker_examples_latest
- name: Main CI - Minimum version examples
if: matrix.dependency-level == 'minimum'
run: cd react_on_rails && bundle exec rake run_rspec:shakapacker_examples_minimum
- name: Store test results
uses: actions/upload-artifact@v4
with:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ jobs:
echo "Node version: "; node -v
echo "Yarn version: "; yarn --version
echo "Bundler version: "; bundle --version
- name: run conversion script to support shakapacker v6
- name: Run conversion script for older Node compatibility
if: matrix.dependency-level == 'minimum'
run: script/convert
- name: Install Node modules with Yarn for renderer package
Expand Down Expand Up @@ -211,7 +211,7 @@ jobs:
echo "Node version: "; node -v
echo "Yarn version: "; yarn --version
echo "Bundler version: "; bundle --version
- name: run conversion script to support shakapacker v6
- name: Run conversion script for older Node compatibility
if: matrix.dependency-level == 'minimum'
run: script/convert
- name: Save root ruby gems to cache
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@
"prettier": "^3.5.2",
"prop-types": "^15.8.1",
"publint": "^0.3.4",
"react": "18.0.0",
"react-dom": "18.0.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-on-rails-rsc": "19.0.2",
"redux": "^4.2.1",
"stylelint": "^16.14.0",
Expand Down
14 changes: 12 additions & 2 deletions react_on_rails/rakelib/example_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,22 @@ def self.all
@all ||= { shakapacker_examples: [] }
end

attr_reader :packer_type, :name, :generator_options
# Minimum supported versions for compatibility testing
MINIMUM_REACT_VERSION = "18.0.0"
MINIMUM_SHAKAPACKER_VERSION = "8.2.0"

def initialize(packer_type: nil, name: nil, generator_options: nil)
attr_reader :packer_type, :name, :generator_options, :minimum_versions

# Ruby convention: predicate method with ? suffix for boolean checks
def minimum_versions?
minimum_versions
end

def initialize(packer_type: nil, name: nil, generator_options: nil, minimum_versions: false)
@packer_type = packer_type
@name = name
@generator_options = generator_options
@minimum_versions = minimum_versions
self.class.all[packer_type.to_sym] << self
end

Expand Down
7 changes: 7 additions & 0 deletions react_on_rails/rakelib/examples_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,10 @@ example_type_data:
generator_options: --redux
- name: redux-server-rendering
generator_options: --redux --example-server-rendering
# Minimum version compatibility tests - tests React 18 and Shakapacker 8.2.0
- name: basic-minimum
generator_options: ''
minimum_versions: true
- name: basic-server-rendering-minimum
generator_options: --example-server-rendering
minimum_versions: true
19 changes: 19 additions & 0 deletions react_on_rails/rakelib/run_rspec.rake
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,25 @@ namespace :run_rspec do
ExampleType.all[:shakapacker_examples].each { |example_type| Rake::Task[example_type.rspec_task_name].invoke }
end

# Helper methods for filtering examples
def latest_examples
ExampleType.all[:shakapacker_examples].reject(&:minimum_versions?)
end

def minimum_examples
ExampleType.all[:shakapacker_examples].select(&:minimum_versions?)
end

desc "Runs Rspec for latest version example apps only (excludes minimum version tests)"
task shakapacker_examples_latest: latest_examples.map(&:gen_task_name) do
latest_examples.each { |example_type| Rake::Task[example_type.rspec_task_name].invoke }
end

desc "Runs Rspec for minimum version example apps only (React 18, Shakapacker 8.2.0)"
task shakapacker_examples_minimum: minimum_examples.map(&:gen_task_name) do
minimum_examples.each { |example_type| Rake::Task[example_type.rspec_task_name].invoke }
end

Coveralls::RakeTask.new if ENV["USE_COVERALLS"] == "TRUE"

desc "run all tests no examples"
Expand Down
36 changes: 36 additions & 0 deletions react_on_rails/rakelib/shakapacker_examples.rake
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,45 @@
require "yaml"
require "rails/version"
require "pathname"
require "json"

require_relative "example_type"
require_relative "task_helpers"

namespace :shakapacker_examples do # rubocop:disable Metrics/BlockLength
include ReactOnRails::TaskHelpers

# Updates package.json to use minimum supported versions for compatibility testing
def apply_minimum_versions(dir) # rubocop:disable Metrics/CyclomaticComplexity
package_json_path = File.join(dir, "package.json")
return unless File.exist?(package_json_path)

begin
package_json = JSON.parse(File.read(package_json_path))
rescue JSON::ParserError => e
puts " ERROR: Failed to parse package.json in #{dir}: #{e.message}"
raise
end

# Update React versions to minimum supported
if package_json["dependencies"]
package_json["dependencies"]["react"] = ExampleType::MINIMUM_REACT_VERSION
package_json["dependencies"]["react-dom"] = ExampleType::MINIMUM_REACT_VERSION
end

# Update Shakapacker to minimum supported version
if package_json["devDependencies"]&.key?("shakapacker")
package_json["devDependencies"]["shakapacker"] = ExampleType::MINIMUM_SHAKAPACKER_VERSION
elsif package_json["dependencies"]&.key?("shakapacker")
package_json["dependencies"]["shakapacker"] = ExampleType::MINIMUM_SHAKAPACKER_VERSION
end

File.write(package_json_path, "#{JSON.pretty_generate(package_json)}\n")
puts " Updated package.json with minimum versions:"
puts " React: #{ExampleType::MINIMUM_REACT_VERSION}"
puts " Shakapacker: #{ExampleType::MINIMUM_SHAKAPACKER_VERSION}"
end

# Define tasks for each example type
ExampleType.all[:shakapacker_examples].each do |example_type|
relative_gem_root = Pathname(gem_root).relative_path_from(Pathname(example_type.dir))
Expand Down Expand Up @@ -46,6 +78,10 @@ namespace :shakapacker_examples do # rubocop:disable Metrics/BlockLength
"REACT_ON_RAILS_SKIP_VALIDATION=true #{cmd}"
end
sh_in_dir(example_type.dir, generator_commands)

# Apply minimum versions for compatibility testing examples
apply_minimum_versions(example_type.dir) if example_type.minimum_versions

sh_in_dir(example_type.dir, "npm install")
# Generate the component packs after running the generator to ensure all
# auto-bundled components have corresponding pack files created
Expand Down
4 changes: 2 additions & 2 deletions react_on_rails/spec/dummy/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
"node-libs-browser": "^2.2.1",
"null-loader": "^4.0.0",
"prop-types": "^15.7.2",
"react": "18.0.0",
"react-dom": "18.0.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-helmet": "^6.1.0",
"react-on-rails": "link:.yalc/react-on-rails",
"react-redux": "^8.0.2",
Expand Down
33 changes: 14 additions & 19 deletions react_on_rails/spec/dummy/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4425,7 +4425,7 @@ lodash@^4.17.19, lodash@^4.17.4:
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==

loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
loose-envify@^1.0.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
Expand Down Expand Up @@ -5198,13 +5198,12 @@ [email protected]:
iconv-lite "0.4.24"
unpipe "1.0.0"

react-dom@18.0.0:
version "18.0.0"
resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.0.0.tgz#26b88534f8f1dbb80853e1eabe752f24100d8023"
integrity sha512-XqX7uzmFo0pUceWFCt7Gff6IyIMzFUn7QMZrbrQfGxtaxXZIcGQzoNpRLE3fQLnS4XzLLPMZX2T9TRcSrasicw==
react-dom@^19.0.0:
version "19.2.0"
resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz#00ed1e959c365e9a9d48f8918377465466ec3af8"
integrity sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==
dependencies:
loose-envify "^1.1.0"
scheduler "^0.21.0"
scheduler "^0.27.0"

react-fast-compare@^3.1.1:
version "3.2.0"
Expand Down Expand Up @@ -5272,12 +5271,10 @@ react-side-effect@^2.1.0:
resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.0.tgz#1ce4a8b4445168c487ed24dab886421f74d380d3"
integrity sha512-IgmcegOSi5SNX+2Snh1vqmF0Vg/CbkycU9XZbOHJlZ6kMzTmi3yc254oB1WCkgA7OQtIAoLmcSFuHTc/tlcqXg==

[email protected]:
version "18.0.0"
resolved "https://registry.npmjs.org/react/-/react-18.0.0.tgz#b468736d1f4a5891f38585ba8e8fb29f91c3cb96"
integrity sha512-x+VL6wbT4JRVPm7EGxXhZ8w8LTROaxPXOqhlGyVSrv0sB1jkyFGgXxJ8LVoPRLvPR6/CIZGFmfzqUa2NYeMr2A==
dependencies:
loose-envify "^1.1.0"
react@^19.0.0:
version "19.2.0"
resolved "https://registry.npmjs.org/react/-/react-19.2.0.tgz#d33dd1721698f4376ae57a54098cb47fc75d93a5"
integrity sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==

readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.3.3, readable-stream@^2.3.6:
version "2.3.7"
Expand Down Expand Up @@ -5535,12 +5532,10 @@ sass@^1.43.4:
dependencies:
chokidar ">=3.0.0 <4.0.0"

scheduler@^0.21.0:
version "0.21.0"
resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz#6fd2532ff5a6d877b6edb12f00d8ab7e8f308820"
integrity sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==
dependencies:
loose-envify "^1.1.0"
scheduler@^0.27.0:
version "0.27.0"
resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz#0c4ef82d67d1e5c1e359e8fc76d3a87f045fe5bd"
integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==

schema-utils@^2.6.5:
version "2.7.0"
Expand Down
44 changes: 10 additions & 34 deletions script/convert
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

# This script converts the codebase to use minimum supported dependency versions.
# It's run during CI when testing the "minimum" dependency matrix to ensure
# backward compatibility.
#
# React/Shakapacker version compatibility is now tested through generator smoke tests
# (see examples_config.yml for minimum version examples like basic-minimum).
# This script only handles Node.js/tooling compatibility and Pro package test adjustments.

def gsub_file_content(path, old_content, new_content)
path = File.expand_path(path, __dir__)

Expand All @@ -17,19 +25,6 @@ def gsub_file_content(path, old_content, new_content)
File.binwrite(path, content)
end

def move(old_path, new_path)
old_path = File.expand_path(old_path, __dir__)
new_path = File.expand_path(new_path, __dir__)
File.rename(old_path, new_path)
end

# Keep shakapacker.yml since we're using Shakapacker 8+
# move("../react_on_rails/spec/dummy/config/shakapacker.yml", "../react_on_rails/spec/dummy/config/webpacker.yml")

# Shakapacker - use version with async script loading support (8.2.0+)
gsub_file_content("../react_on_rails/Gemfile.development_dependencies", /gem "shakapacker", "[^"]*"/, 'gem "shakapacker", "8.2.0"')
gsub_file_content("../react_on_rails/spec/dummy/package.json", /"shakapacker": "[^"]*",/, '"shakapacker": "8.2.0",')

# The below packages don't work on the oldest supported Node version and aren't needed there anyway
# Note: All dev dependencies remain in root package.json even after workspace migration
gsub_file_content("../package.json", /"[^"]*eslint[^"]*": "[^"]*",?/, "")
Expand All @@ -42,35 +37,16 @@ gsub_file_content("../package.json", %r{"@testing-library/[^"]*": "[^"]*",}, "")
# Clean up any trailing commas before closing braces
gsub_file_content("../package.json", /,(\s*})/, "\\1")

# Switch to minimum supported React version (React 18 since we removed PropTypes)
gsub_file_content("../package.json", /"react": "[^"]*",/, '"react": "18.0.0",')
gsub_file_content("../package.json", /"react-dom": "[^"]*",/, '"react-dom": "18.0.0",')
gsub_file_content("../react_on_rails/spec/dummy/package.json", /"react": "[^"]*",/, '"react": "18.0.0",')
gsub_file_content("../react_on_rails/spec/dummy/package.json", /"react-dom": "[^"]*",/, '"react-dom": "18.0.0",')
# Pro package: Skip RSC tests on React 18 since RSC requires React 19
gsub_file_content(
"../packages/react-on-rails-pro/package.json",
/"test:non-rsc": "(?:\\"|[^"])*",/,
'"test:non-rsc": "jest tests --testPathIgnorePatterns=\".*(RSC|stream|' \
'registerServerComponent|serverRenderReactComponent|SuspenseHydration).*\"",'
)
# Make test:rsc script do nothing
# Make test:rsc script do nothing on React 18
gsub_file_content(
"../packages/react-on-rails-pro/package.json",
/"test:rsc": "(?:\\"|[^"])*",/,
'"test:rsc": "exit 0",'
)
# Keep modern JSX transform for React 18+
# gsub_file_content("../tsconfig.json", "react-jsx", "react")
# gsub_file_content("../spec/dummy/babel.config.js", "runtime: 'automatic'", "runtime: 'classic'")
# Keep modern ReScript configuration for React 18+
# gsub_file_content("../spec/dummy/rescript.json", '"version": 4', '"version": 4, "mode": "classic"')
# Skip React 16 file replacements since we're using React 18+
# Dir.glob(File.expand_path("../spec/dummy/**/app-react16/**/*.*", __dir__)).each do |file|
# move(file, file.gsub("-react16", ""))
# end

# These replacements were incorrect - generateWebpackConfig() is the correct function from shakapacker
# gsub_file_content("../spec/dummy/config/webpack/commonWebpackConfig.js", /generateWebpackConfig(\(\))?/,
# "webpackConfig")
#
# gsub_file_content("../spec/dummy/config/webpack/webpack.config.js", /generateWebpackConfig(\(\))?/, "webpackConfig")
33 changes: 14 additions & 19 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6272,7 +6272,7 @@ lodash@^4.17.4, lodash@^4.7.0:
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==

loose-envify@^1.1.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
loose-envify@^1.3.1, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
Expand Down Expand Up @@ -7218,13 +7218,12 @@ [email protected]:
iconv-lite "0.4.24"
unpipe "1.0.0"

react-dom@18.0.0:
version "18.0.0"
resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.0.0.tgz#26b88534f8f1dbb80853e1eabe752f24100d8023"
integrity sha512-XqX7uzmFo0pUceWFCt7Gff6IyIMzFUn7QMZrbrQfGxtaxXZIcGQzoNpRLE3fQLnS4XzLLPMZX2T9TRcSrasicw==
react-dom@^19.0.0:
version "19.2.0"
resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz#00ed1e959c365e9a9d48f8918377465466ec3af8"
integrity sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==
dependencies:
loose-envify "^1.1.0"
scheduler "^0.21.0"
scheduler "^0.27.0"

react-is@^16.13.1:
version "16.13.1"
Expand Down Expand Up @@ -7255,12 +7254,10 @@ react-on-rails@*:
resolved "https://registry.npmjs.org/react-on-rails/-/react-on-rails-16.1.2.tgz#27308d7d5806e73354eea2355c5c4036ade443d4"
integrity sha512-SiE168FgUtp9Sld2MAZUs2+R/D7B+SlNGX4zl9T2rB1QluzgTY042a4/i6v+ivQpBIhNjJujlVFIHYp+uVqjoQ==

[email protected]:
version "18.0.0"
resolved "https://registry.npmjs.org/react/-/react-18.0.0.tgz#b468736d1f4a5891f38585ba8e8fb29f91c3cb96"
integrity sha512-x+VL6wbT4JRVPm7EGxXhZ8w8LTROaxPXOqhlGyVSrv0sB1jkyFGgXxJ8LVoPRLvPR6/CIZGFmfzqUa2NYeMr2A==
dependencies:
loose-envify "^1.1.0"
react@^19.0.0:
version "19.2.0"
resolved "https://registry.npmjs.org/react/-/react-19.2.0.tgz#d33dd1721698f4376ae57a54098cb47fc75d93a5"
integrity sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==

readline-sync@^1.4.7:
version "1.4.10"
Expand Down Expand Up @@ -7538,12 +7535,10 @@ saxes@^6.0.0:
dependencies:
xmlchars "^2.2.0"

scheduler@^0.21.0:
version "0.21.0"
resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz#6fd2532ff5a6d877b6edb12f00d8ab7e8f308820"
integrity sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==
dependencies:
loose-envify "^1.1.0"
scheduler@^0.27.0:
version "0.27.0"
resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz#0c4ef82d67d1e5c1e359e8fc76d3a87f045fe5bd"
integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==

secure-json-parse@^4.0.0:
version "4.1.0"
Expand Down
Loading