react_on_rails 16.1.2 → 16.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rspec +2 -0
- data/.rubocop.yml +85 -0
- data/Gemfile.development_dependencies +8 -7
- data/Gemfile.lock +158 -119
- data/Steepfile +56 -0
- data/lib/generators/react_on_rails/base_generator.rb +43 -120
- data/lib/generators/react_on_rails/dev_tests_generator.rb +2 -1
- data/lib/generators/react_on_rails/generator_helper.rb +102 -2
- data/lib/generators/react_on_rails/install_generator.rb +36 -156
- data/lib/generators/react_on_rails/js_dependency_manager.rb +383 -0
- data/lib/generators/react_on_rails/templates/base/base/.dev-services.yml.example +76 -0
- data/lib/generators/react_on_rails/templates/base/base/bin/shakapacker-precompile-hook +30 -0
- data/lib/generators/react_on_rails/templates/base/base/bin/switch-bundler +141 -0
- data/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt +44 -45
- data/lib/generators/react_on_rails/templates/base/base/config/{shakapacker.yml → shakapacker.yml.tt} +28 -3
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/development.js.tt +15 -9
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt +42 -6
- data/lib/react_on_rails/configuration.rb +149 -32
- data/lib/react_on_rails/controller.rb +3 -3
- data/lib/react_on_rails/dev/pack_generator.rb +168 -2
- data/lib/react_on_rails/dev/process_manager.rb +136 -14
- data/lib/react_on_rails/dev/server_manager.rb +194 -26
- data/lib/react_on_rails/dev/service_checker.rb +200 -0
- data/lib/react_on_rails/doctor.rb +341 -12
- data/lib/react_on_rails/engine.rb +75 -1
- data/lib/react_on_rails/git_utils.rb +3 -1
- data/lib/react_on_rails/helper.rb +70 -192
- data/lib/react_on_rails/locales/base.rb +17 -5
- data/lib/react_on_rails/packer_utils.rb +79 -2
- data/lib/react_on_rails/packs_generator.rb +57 -39
- data/lib/react_on_rails/prerender_error.rb +74 -17
- data/lib/react_on_rails/pro_helper.rb +64 -0
- data/lib/react_on_rails/react_component/render_options.rb +7 -7
- data/lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb +2 -5
- data/lib/react_on_rails/smart_error.rb +326 -0
- data/lib/react_on_rails/system_checker.rb +8 -9
- data/lib/react_on_rails/test_helper/webpack_assets_status_checker.rb +16 -7
- data/lib/react_on_rails/utils.rb +241 -55
- data/lib/react_on_rails/version.rb +1 -1
- data/lib/react_on_rails/version_checker.rb +383 -35
- data/lib/tasks/generate_packs.rake +12 -6
- data/lib/tasks/locale.rake +6 -1
- data/rakelib/docker.rake +26 -0
- data/rakelib/dummy_apps.rake +30 -0
- data/rakelib/example_type.rb +121 -0
- data/rakelib/examples_config.yml +52 -0
- data/rakelib/lint.rake +52 -0
- data/rakelib/node_package.rake +15 -0
- data/rakelib/rbs.rake +70 -0
- data/rakelib/run_rspec.rake +223 -0
- data/rakelib/shakapacker_examples.rake +171 -0
- data/rakelib/task_helpers.rb +134 -0
- data/rakelib/update_changelog.rake +73 -0
- data/react_on_rails.gemspec +4 -3
- data/sig/README.md +52 -0
- data/sig/react_on_rails/configuration.rbs +96 -0
- data/sig/react_on_rails/controller.rbs +15 -0
- data/sig/react_on_rails/dev/file_manager.rbs +15 -0
- data/sig/react_on_rails/dev/pack_generator.rbs +19 -0
- data/sig/react_on_rails/dev/process_manager.rbs +22 -0
- data/sig/react_on_rails/dev/server_manager.rbs +39 -0
- data/sig/react_on_rails/dev/service_checker.rbs +22 -0
- data/sig/react_on_rails/error.rbs +4 -0
- data/sig/react_on_rails/generators/js_dependency_manager.rbs +123 -0
- data/sig/react_on_rails/git_utils.rbs +8 -0
- data/sig/react_on_rails/helper.rbs +65 -0
- data/sig/react_on_rails/json_parse_error.rbs +10 -0
- data/sig/react_on_rails/locales.rbs +46 -0
- data/sig/react_on_rails/packer_utils.rbs +15 -0
- data/sig/react_on_rails/prerender_error.rbs +21 -0
- data/sig/react_on_rails/server_rendering_pool.rbs +12 -0
- data/sig/react_on_rails/smart_error.rbs +28 -0
- data/sig/react_on_rails/test_helper.rbs +11 -0
- data/sig/react_on_rails/utils.rbs +34 -0
- data/sig/react_on_rails/version_checker.rbs +12 -0
- data/sig/react_on_rails.rbs +17 -0
- metadata +49 -32
- data/AI_AGENT_INSTRUCTIONS.md +0 -63
- data/CHANGELOG.md +0 -1836
- data/CLAUDE.md +0 -135
- data/CODING_AGENTS.md +0 -313
- data/CONTRIBUTING.md +0 -668
- data/Dockerfile_tests +0 -12
- data/KUDOS.md +0 -114
- data/LICENSE.md +0 -47
- data/LICENSES/README.md +0 -14
- data/NEWS.md +0 -62
- data/PROJECTS.md +0 -63
- data/REACT-ON-RAILS-PRO-LICENSE.md +0 -129
- data/README.md +0 -217
- data/SUMMARY.md +0 -88
- data/TODO.md +0 -135
- data/bin/lefthook/check-trailing-newlines +0 -38
- data/bin/lefthook/get-changed-files +0 -26
- data/bin/lefthook/prettier-format +0 -26
- data/bin/lefthook/ruby-autofix +0 -26
- data/bin/lefthook/ruby-lint +0 -27
- data/docker-compose.yml +0 -11
- data/eslint.config.ts +0 -232
- data/knip.ts +0 -114
- data/lib/react_on_rails/pro/NOTICE +0 -21
- data/lib/react_on_rails/pro/helper.rb +0 -122
- data/lib/react_on_rails/pro/utils.rb +0 -53
- data/tsconfig.eslint.json +0 -6
- data/tsconfig.json +0 -19
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rake"
|
|
4
|
+
|
|
5
|
+
require_relative "task_helpers"
|
|
6
|
+
require_relative File.join(__dir__, "..", "lib", "react_on_rails", "utils")
|
|
7
|
+
|
|
8
|
+
# Defines the ExampleType class, where each object represents a unique type of example
|
|
9
|
+
# app that we can generate.
|
|
10
|
+
module ReactOnRails
|
|
11
|
+
module TaskHelpers
|
|
12
|
+
class ExampleType
|
|
13
|
+
def self.all
|
|
14
|
+
@all ||= { shakapacker_examples: [] }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Supported React versions for compatibility testing
|
|
18
|
+
# Keys are major version strings, values are specific version to pin to (nil = latest)
|
|
19
|
+
REACT_VERSIONS = {
|
|
20
|
+
"16" => "16.14.0",
|
|
21
|
+
"17" => "17.0.2",
|
|
22
|
+
"18" => "18.0.0",
|
|
23
|
+
"19" => nil # nil means use latest (default)
|
|
24
|
+
}.freeze
|
|
25
|
+
|
|
26
|
+
# Supported React major versions (we test with latest patch of each)
|
|
27
|
+
MINIMUM_SUPPORTED_REACT_MAJOR_VERSION = "16"
|
|
28
|
+
LATEST_REACT_MAJOR_VERSION = "19"
|
|
29
|
+
|
|
30
|
+
# Minimum Shakapacker version for compatibility testing
|
|
31
|
+
MINIMUM_SHAKAPACKER_VERSION = "8.2.0"
|
|
32
|
+
|
|
33
|
+
attr_reader :packer_type, :name, :generator_options, :react_version
|
|
34
|
+
|
|
35
|
+
# Returns true if this example uses a pinned (non-latest) React version
|
|
36
|
+
def pinned_react_version?
|
|
37
|
+
!react_version.nil?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Returns the actual React version string to use
|
|
41
|
+
def react_version_string
|
|
42
|
+
return nil unless react_version
|
|
43
|
+
|
|
44
|
+
REACT_VERSIONS[react_version.to_s] || react_version
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def initialize(packer_type: nil, name: nil, generator_options: nil, react_version: nil)
|
|
48
|
+
@packer_type = packer_type
|
|
49
|
+
@name = name
|
|
50
|
+
@generator_options = generator_options
|
|
51
|
+
@react_version = react_version
|
|
52
|
+
|
|
53
|
+
# Validate react_version is a known version to catch configuration errors early
|
|
54
|
+
if @react_version && !REACT_VERSIONS.key?(@react_version.to_s)
|
|
55
|
+
valid_versions = REACT_VERSIONS.keys.join(", ")
|
|
56
|
+
raise ArgumentError, "Invalid react_version '#{@react_version}' for example '#{name}'. " \
|
|
57
|
+
"Valid versions: #{valid_versions}"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
self.class.all[packer_type.to_sym] << self
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def name_pretty
|
|
64
|
+
"#{@name} example app"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def dir
|
|
68
|
+
File.join(examples_dir, name)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def dir_exist?
|
|
72
|
+
Dir.exist?(dir)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def gemfile
|
|
76
|
+
File.join(dir, "Gemfile")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Options we pass when running `rails new` from the command-line.
|
|
80
|
+
attr_writer :rails_options
|
|
81
|
+
|
|
82
|
+
def rails_options
|
|
83
|
+
@rails_options ||= if ReactOnRails::Utils.rails_version_less_than("7.0")
|
|
84
|
+
"--skip-bundle --skip-spring --skip-git --skip-test-unit --skip-active-record -J"
|
|
85
|
+
else
|
|
86
|
+
"--skip-bundle --skip-spring --skip-git --skip-test-unit --skip-active-record -j webpack"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
%w[gen clobber npm_install build_webpack_bundles].each do |task_type|
|
|
91
|
+
method_name_normal = "#{task_type}_task_name" # ex: `clean_task_name`
|
|
92
|
+
method_name_short = "#{method_name_normal}_short" # ex: `clean_task_name_short`
|
|
93
|
+
|
|
94
|
+
define_method(method_name_normal) { "#{@packer_type}:#{task_type}_#{name}" }
|
|
95
|
+
define_method(method_name_short) { "#{task_type}_#{name}" }
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def rspec_task_name_short
|
|
99
|
+
"#{packer_type}_#{name}"
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def rspec_task_name
|
|
103
|
+
"run_rspec:#{rspec_task_name_short}"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Assumes we are inside a rails app's folder and necessary gems have been installed
|
|
107
|
+
def generator_shell_commands
|
|
108
|
+
shell_commands = []
|
|
109
|
+
shell_commands << "rails generate react_on_rails:install #{generator_options} --ignore-warnings --force"
|
|
110
|
+
shell_commands << "rails generate react_on_rails:dev_tests #{generator_options}"
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
private
|
|
114
|
+
|
|
115
|
+
# Defines globs that scoop up all files (including dotfiles) in given directory
|
|
116
|
+
def all_files_in_dir(p_dir)
|
|
117
|
+
[File.join(p_dir, "**", "*"), File.join(p_dir, "**", ".*")]
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Example Type Configuration for React on Rails Generator Tests
|
|
2
|
+
#
|
|
3
|
+
# CI Test Coverage:
|
|
4
|
+
# -----------------
|
|
5
|
+
# - Latest CI (all PRs): Runs shakapacker_examples_latest (React 19, Shakapacker 9.x)
|
|
6
|
+
# Examples: basic, basic-server-rendering, redux, redux-server-rendering
|
|
7
|
+
#
|
|
8
|
+
# - Pinned CI (master): Runs shakapacker_examples_pinned (React 16, 17, 18 with Shakapacker 8.2.0)
|
|
9
|
+
# Examples: basic-react16, basic-server-rendering-react16,
|
|
10
|
+
# basic-react17, basic-server-rendering-react17,
|
|
11
|
+
# basic-react18, basic-server-rendering-react18
|
|
12
|
+
#
|
|
13
|
+
# Terminology:
|
|
14
|
+
# - "Latest" = Current React version (19) with latest Shakapacker (9.x)
|
|
15
|
+
# - "Pinned" = Specific older React versions (16, 17, 18) for backward compatibility testing
|
|
16
|
+
#
|
|
17
|
+
# Note: We support React 16+ but test with latest patch of each major version.
|
|
18
|
+
|
|
19
|
+
example_type_data:
|
|
20
|
+
# Latest versions (React 19, Shakapacker 9.x)
|
|
21
|
+
- name: basic
|
|
22
|
+
generator_options: ''
|
|
23
|
+
- name: basic-server-rendering
|
|
24
|
+
generator_options: --example-server-rendering
|
|
25
|
+
- name: redux
|
|
26
|
+
generator_options: --redux
|
|
27
|
+
- name: redux-server-rendering
|
|
28
|
+
generator_options: --redux --example-server-rendering
|
|
29
|
+
|
|
30
|
+
# React 18 compatibility tests (uses Root API introduced in React 18)
|
|
31
|
+
- name: basic-react18
|
|
32
|
+
generator_options: ''
|
|
33
|
+
react_version: '18'
|
|
34
|
+
- name: basic-server-rendering-react18
|
|
35
|
+
generator_options: --example-server-rendering
|
|
36
|
+
react_version: '18'
|
|
37
|
+
|
|
38
|
+
# React 17 compatibility tests (legacy render/hydrate API)
|
|
39
|
+
- name: basic-react17
|
|
40
|
+
generator_options: ''
|
|
41
|
+
react_version: '17'
|
|
42
|
+
- name: basic-server-rendering-react17
|
|
43
|
+
generator_options: --example-server-rendering
|
|
44
|
+
react_version: '17'
|
|
45
|
+
|
|
46
|
+
# React 16 compatibility tests (oldest supported legacy API)
|
|
47
|
+
- name: basic-react16
|
|
48
|
+
generator_options: ''
|
|
49
|
+
react_version: '16'
|
|
50
|
+
- name: basic-server-rendering-react16
|
|
51
|
+
generator_options: --example-server-rendering
|
|
52
|
+
react_version: '16'
|
data/rakelib/lint.rake
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "task_helpers"
|
|
4
|
+
|
|
5
|
+
namespace :lint do # rubocop:disable Metrics/BlockLength
|
|
6
|
+
include ReactOnRails::TaskHelpers
|
|
7
|
+
|
|
8
|
+
desc "Run Rubocop as shell"
|
|
9
|
+
task :rubocop do
|
|
10
|
+
sh_in_dir(gem_root, "bundle exec rubocop --version", "bundle exec rubocop .")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
desc "Run stylelint as shell"
|
|
14
|
+
task :scss do
|
|
15
|
+
sh_in_dir(gem_root, stylelint_command)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
desc "Run eslint as shell"
|
|
19
|
+
task :eslint do
|
|
20
|
+
sh_in_dir(gem_root, "pnpm run eslint --version", "pnpm run eslint .")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
desc "Run all eslint, rubocop & stylelint linters"
|
|
24
|
+
task lint: %i[eslint rubocop scss] do
|
|
25
|
+
puts "Completed all linting"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
desc "Auto-fix all linting violations"
|
|
29
|
+
task :autofix do
|
|
30
|
+
sh_in_dir(gem_root, "pnpm run eslint . --fix")
|
|
31
|
+
sh_in_dir(gem_root, "pnpm run prettier --write .")
|
|
32
|
+
sh_in_dir(gem_root, stylelint_fix_command)
|
|
33
|
+
sh_in_dir(gem_root, "bundle exec rubocop -A")
|
|
34
|
+
puts "Completed auto-fixing all linting violations"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def stylelint_command
|
|
40
|
+
"pnpm run stylelint \"spec/dummy/app/assets/stylesheets/**/*.scss\" \"spec/dummy/client/**/*.scss\""
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def stylelint_fix_command
|
|
44
|
+
"#{stylelint_command} --fix"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
desc "Runs all linters. Run `rake -D lint` to see all available lint options"
|
|
49
|
+
task lint: ["lint:lint"]
|
|
50
|
+
|
|
51
|
+
desc "Auto-fix all linting violations (eslint --fix, prettier --write, rubocop -A)"
|
|
52
|
+
task autofix: ["lint:autofix"]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "task_helpers"
|
|
4
|
+
|
|
5
|
+
namespace :node_package do
|
|
6
|
+
include ReactOnRails::TaskHelpers
|
|
7
|
+
|
|
8
|
+
task :build do
|
|
9
|
+
puts "Building Node Package and running 'yalc publish'"
|
|
10
|
+
sh "pnpm run build && pnpm yalc:publish"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
desc "Prepares node_package by building and symlinking any example/dummy apps present"
|
|
15
|
+
task node_package: "node_package:build"
|
data/rakelib/rbs.rake
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "open3"
|
|
4
|
+
require "timeout"
|
|
5
|
+
|
|
6
|
+
# rubocop:disable Metrics/BlockLength
|
|
7
|
+
namespace :rbs do
|
|
8
|
+
desc "Validate RBS type signatures"
|
|
9
|
+
task :validate do
|
|
10
|
+
require "rbs"
|
|
11
|
+
require "rbs/cli"
|
|
12
|
+
|
|
13
|
+
puts "Validating RBS type signatures..."
|
|
14
|
+
|
|
15
|
+
# Use Open3 for better error handling - captures stdout, stderr, and exit status separately
|
|
16
|
+
# This allows us to distinguish between actual validation errors and warnings
|
|
17
|
+
# Note: Must use bundle exec even though rake runs in bundle context because
|
|
18
|
+
# spawned shell commands via Open3.capture3() do NOT inherit bundle context
|
|
19
|
+
# Wrap in Timeout to prevent hung processes in CI environments (60 second timeout)
|
|
20
|
+
stdout, stderr, status = Timeout.timeout(60) do
|
|
21
|
+
Open3.capture3("bundle exec rbs -I sig validate")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
if status.success?
|
|
25
|
+
puts "✓ RBS validation passed"
|
|
26
|
+
else
|
|
27
|
+
puts "✗ RBS validation failed"
|
|
28
|
+
puts stdout unless stdout.empty?
|
|
29
|
+
warn stderr unless stderr.empty?
|
|
30
|
+
exit 1
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
desc "Check RBS type signatures (alias for validate)"
|
|
35
|
+
task check: :validate
|
|
36
|
+
|
|
37
|
+
desc "List all RBS files"
|
|
38
|
+
task :list do
|
|
39
|
+
sig_files = Dir.glob("sig/**/*.rbs")
|
|
40
|
+
puts "RBS type signature files:"
|
|
41
|
+
sig_files.each { |f| puts " #{f}" }
|
|
42
|
+
puts "\nTotal: #{sig_files.count} files"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
desc "Run Steep type checker"
|
|
46
|
+
task :steep do
|
|
47
|
+
puts "Running Steep type checker..."
|
|
48
|
+
|
|
49
|
+
# Use Open3 for better error handling
|
|
50
|
+
# Note: Must use bundle exec even though rake runs in bundle context because
|
|
51
|
+
# spawned shell commands via Open3.capture3() do NOT inherit bundle context
|
|
52
|
+
# Wrap in Timeout to prevent hung processes in CI environments (60 second timeout)
|
|
53
|
+
stdout, stderr, status = Timeout.timeout(60) do
|
|
54
|
+
Open3.capture3("bundle exec steep check")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
if status.success?
|
|
58
|
+
puts "✓ Steep type checking passed"
|
|
59
|
+
else
|
|
60
|
+
puts "✗ Steep type checking failed"
|
|
61
|
+
puts stdout unless stdout.empty?
|
|
62
|
+
warn stderr unless stderr.empty?
|
|
63
|
+
exit 1
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
desc "Run all RBS checks (validate + steep)"
|
|
68
|
+
task all: %i[validate steep]
|
|
69
|
+
end
|
|
70
|
+
# rubocop:enable Metrics/BlockLength
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "coveralls/rake/task" if ENV["USE_COVERALLS"] == "TRUE"
|
|
4
|
+
|
|
5
|
+
require "pathname"
|
|
6
|
+
require "yaml"
|
|
7
|
+
|
|
8
|
+
require_relative "task_helpers"
|
|
9
|
+
require_relative "example_type"
|
|
10
|
+
|
|
11
|
+
# rubocop:disable Metrics/BlockLength
|
|
12
|
+
namespace :run_rspec do
|
|
13
|
+
include ReactOnRails::TaskHelpers
|
|
14
|
+
|
|
15
|
+
# Loads data from examples_config.yml and instantiates corresponding ExampleType objects
|
|
16
|
+
examples_config_file = File.expand_path("examples_config.yml", __dir__)
|
|
17
|
+
examples_config = symbolize_keys(YAML.safe_load_file(examples_config_file))
|
|
18
|
+
examples_config[:example_type_data].each do |example_type_data|
|
|
19
|
+
ExampleType.new(packer_type: "shakapacker_examples", **symbolize_keys(example_type_data))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
spec_dummy_dir = File.join(gem_root, "spec", "dummy")
|
|
23
|
+
|
|
24
|
+
# RBS Runtime Type Checking Configuration
|
|
25
|
+
# ========================================
|
|
26
|
+
# Runtime type checking is ENABLED BY DEFAULT when RBS gem is available
|
|
27
|
+
# Use ENV["DISABLE_RBS_RUNTIME_CHECKING"] = "true" to disable
|
|
28
|
+
#
|
|
29
|
+
# Coverage Strategy:
|
|
30
|
+
# - :gem task - Enables checking for ReactOnRails::* (direct gem unit tests)
|
|
31
|
+
# - :dummy tasks - Enables checking (integration tests exercise gem code paths)
|
|
32
|
+
# - :example tasks - No checking (examples are user-facing demo apps)
|
|
33
|
+
#
|
|
34
|
+
# Rationale per Evil Martians best practices:
|
|
35
|
+
# Runtime checking catches type errors in actual execution paths that static
|
|
36
|
+
# analysis might miss. Dummy/integration tests exercise more code paths than
|
|
37
|
+
# unit tests alone, providing comprehensive type safety validation.
|
|
38
|
+
def rbs_runtime_env_vars
|
|
39
|
+
return "" if ENV["DISABLE_RBS_RUNTIME_CHECKING"] == "true"
|
|
40
|
+
|
|
41
|
+
begin
|
|
42
|
+
require "rbs"
|
|
43
|
+
# Preserve existing RUBYOPT flags (e.g., --enable-yjit, --jit, warnings toggles)
|
|
44
|
+
# by appending RBS runtime hook instead of replacing
|
|
45
|
+
existing_rubyopt = ENV.fetch("RUBYOPT", nil)
|
|
46
|
+
rubyopt_parts = ["-rrbs/test/setup", existing_rubyopt].compact.reject(&:empty?)
|
|
47
|
+
"RBS_TEST_TARGET='ReactOnRails::*' RUBYOPT='#{rubyopt_parts.join(' ')}'"
|
|
48
|
+
rescue LoadError
|
|
49
|
+
# RBS not available - silently skip runtime checking
|
|
50
|
+
# This is expected in environments without the rbs gem
|
|
51
|
+
""
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
desc "Run RSpec for top level only"
|
|
56
|
+
task :gem do
|
|
57
|
+
run_tests_in("",
|
|
58
|
+
rspec_args: File.join("spec", "react_on_rails"),
|
|
59
|
+
env_vars: rbs_runtime_env_vars)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
desc "Runs dummy rspec with turbolinks"
|
|
63
|
+
task dummy: ["dummy_apps:dummy_app"] do
|
|
64
|
+
run_tests_in(spec_dummy_dir,
|
|
65
|
+
env_vars: rbs_runtime_env_vars)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
desc "Runs dummy rspec without turbolinks"
|
|
69
|
+
task dummy_no_turbolinks: ["dummy_apps:dummy_app"] do
|
|
70
|
+
# Build env vars array for robustness with complex environment variables
|
|
71
|
+
env_vars_array = []
|
|
72
|
+
env_vars_array << rbs_runtime_env_vars unless rbs_runtime_env_vars.empty?
|
|
73
|
+
env_vars_array << "DISABLE_TURBOLINKS=TRUE"
|
|
74
|
+
env_vars = env_vars_array.join(" ")
|
|
75
|
+
run_tests_in(spec_dummy_dir,
|
|
76
|
+
env_vars: env_vars,
|
|
77
|
+
command_name: "dummy_no_turbolinks")
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Dynamically define Rake tasks for each example app found in the examples directory
|
|
81
|
+
ExampleType.all[:shakapacker_examples].each do |example_type|
|
|
82
|
+
puts "Creating #{example_type.rspec_task_name} task"
|
|
83
|
+
desc "Runs RSpec for #{example_type.name_pretty} only"
|
|
84
|
+
task example_type.rspec_task_name_short => example_type.gen_task_name do
|
|
85
|
+
# Use unbundled mode for pinned React version examples to ensure the example app's
|
|
86
|
+
# Gemfile and gem versions are used, not the parent workspace's bundle
|
|
87
|
+
run_tests_in(File.join(examples_dir, example_type.name),
|
|
88
|
+
unbundled: example_type.pinned_react_version?)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
desc "Runs Rspec for shakapacker example apps only"
|
|
93
|
+
task shakapacker_examples: "shakapacker_examples:gen_all" do
|
|
94
|
+
ExampleType.all[:shakapacker_examples].each { |example_type| Rake::Task[example_type.rspec_task_name].invoke }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Helper methods for filtering examples by React version
|
|
98
|
+
def latest_examples
|
|
99
|
+
ExampleType.all[:shakapacker_examples].reject(&:pinned_react_version?)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def react18_examples
|
|
103
|
+
ExampleType.all[:shakapacker_examples].select { |e| e.react_version == "18" }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def react17_examples
|
|
107
|
+
ExampleType.all[:shakapacker_examples].select { |e| e.react_version == "17" }
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def react16_examples
|
|
111
|
+
ExampleType.all[:shakapacker_examples].select { |e| e.react_version == "16" }
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def pinned_version_examples
|
|
115
|
+
ExampleType.all[:shakapacker_examples].select(&:pinned_react_version?)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
desc "Runs Rspec for latest version example apps only (React 19, Shakapacker 9.x)"
|
|
119
|
+
task shakapacker_examples_latest: latest_examples.map(&:gen_task_name) do
|
|
120
|
+
latest_examples.each { |example_type| Rake::Task[example_type.rspec_task_name].invoke }
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
desc "Runs Rspec for React 18 example apps only (Shakapacker 8.2.0)"
|
|
124
|
+
task shakapacker_examples_react18: react18_examples.map(&:gen_task_name) do
|
|
125
|
+
react18_examples.each { |example_type| Rake::Task[example_type.rspec_task_name].invoke }
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
desc "Runs Rspec for React 17 example apps only (legacy render API)"
|
|
129
|
+
task shakapacker_examples_react17: react17_examples.map(&:gen_task_name) do
|
|
130
|
+
react17_examples.each { |example_type| Rake::Task[example_type.rspec_task_name].invoke }
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
desc "Runs Rspec for React 16 example apps only (oldest supported legacy API)"
|
|
134
|
+
task shakapacker_examples_react16: react16_examples.map(&:gen_task_name) do
|
|
135
|
+
react16_examples.each { |example_type| Rake::Task[example_type.rspec_task_name].invoke }
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
desc "Runs Rspec for all pinned version example apps (React 16, 17, and 18)"
|
|
139
|
+
task shakapacker_examples_pinned: pinned_version_examples.map(&:gen_task_name) do
|
|
140
|
+
pinned_version_examples.each { |example_type| Rake::Task[example_type.rspec_task_name].invoke }
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
Coveralls::RakeTask.new if ENV["USE_COVERALLS"] == "TRUE"
|
|
144
|
+
|
|
145
|
+
desc "run all tests no examples"
|
|
146
|
+
task all_but_examples: %i[gem dummy_no_turbolinks dummy js_tests] do
|
|
147
|
+
puts "Completed all RSpec tests"
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
desc "run all dummy tests"
|
|
151
|
+
task all_dummy: %i[dummy_no_turbolinks dummy] do
|
|
152
|
+
puts "Completed all RSpec tests"
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
desc "run all tests"
|
|
156
|
+
task :run_rspec, [:packer] => ["all_but_examples"] do
|
|
157
|
+
Rake::Task["run_rspec:#{packer}_examples"].invoke
|
|
158
|
+
puts "Completed all RSpec tests"
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
# rubocop:enable Metrics/BlockLength
|
|
162
|
+
|
|
163
|
+
desc "js tests (same as 'pnpm run test')"
|
|
164
|
+
task :js_tests do
|
|
165
|
+
sh "pnpm run test"
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
msg = <<~DESC
|
|
169
|
+
Runs all tests, run `rake -D run_rspec` to see all available test options.
|
|
170
|
+
"rake run_rspec:example_basic" is a good way to run only one generator test.
|
|
171
|
+
DESC
|
|
172
|
+
desc msg
|
|
173
|
+
task run_rspec: ["run_rspec:run_rspec"]
|
|
174
|
+
|
|
175
|
+
def calc_path(dir)
|
|
176
|
+
if dir.is_a?(String)
|
|
177
|
+
if dir.start_with?(File::SEPARATOR)
|
|
178
|
+
Pathname.new(dir)
|
|
179
|
+
else
|
|
180
|
+
Pathname.new(File.join(gem_root, dir))
|
|
181
|
+
end
|
|
182
|
+
else
|
|
183
|
+
dir
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Runs rspec in the given directory.
|
|
188
|
+
# If string is passed and it's not absolute, it's converted relative to root of the gem.
|
|
189
|
+
# TEST_ENV_COMMAND_NAME is used to make SimpleCov.command_name unique in order to
|
|
190
|
+
# prevent a name collision. Defaults to the given directory's name.
|
|
191
|
+
# Options:
|
|
192
|
+
# :command_name - name for SimpleCov (default: dir basename)
|
|
193
|
+
# :rspec_args - additional rspec arguments (default: "")
|
|
194
|
+
# :env_vars - additional environment variables (default: "")
|
|
195
|
+
# :unbundled - run with unbundled_sh_in_dir for Bundler isolation (default: false)
|
|
196
|
+
# This is required for pinned version examples because they have different
|
|
197
|
+
# gem versions (e.g., Shakapacker 8.2.0) pinned in their Gemfile than the
|
|
198
|
+
# parent workspace (Shakapacker 9.x). Without bundle isolation, Bundler
|
|
199
|
+
# would inherit the parent's gem resolution and use the wrong versions.
|
|
200
|
+
# Latest version examples don't need this because they use the same versions
|
|
201
|
+
# as the parent workspace.
|
|
202
|
+
def run_tests_in(dir, options = {})
|
|
203
|
+
path = calc_path(dir)
|
|
204
|
+
|
|
205
|
+
command_name = options.fetch(:command_name, path.basename)
|
|
206
|
+
rspec_args = options.fetch(:rspec_args, "")
|
|
207
|
+
unbundled = options.fetch(:unbundled, false)
|
|
208
|
+
|
|
209
|
+
# Build environment variables as an array for proper spacing
|
|
210
|
+
env_tokens = []
|
|
211
|
+
env_tokens << options.fetch(:env_vars, "").strip unless options.fetch(:env_vars, "").strip.empty?
|
|
212
|
+
env_tokens << "TEST_ENV_COMMAND_NAME=\"#{command_name}\""
|
|
213
|
+
env_tokens << "COVERAGE=true" if ENV["USE_COVERALLS"]
|
|
214
|
+
|
|
215
|
+
env_vars = env_tokens.join(" ")
|
|
216
|
+
command = "#{env_vars} bundle exec rspec #{rspec_args}"
|
|
217
|
+
|
|
218
|
+
if unbundled
|
|
219
|
+
unbundled_sh_in_dir(path.realpath, command)
|
|
220
|
+
else
|
|
221
|
+
sh_in_dir(path.realpath, command)
|
|
222
|
+
end
|
|
223
|
+
end
|