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,171 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Defines tasks related to generating example apps using the gem's generator.
|
|
4
|
+
# Allows us to create and test apps generated using a wide range of options.
|
|
5
|
+
#
|
|
6
|
+
# Also see example_type.rb
|
|
7
|
+
|
|
8
|
+
require "yaml"
|
|
9
|
+
require "rails/version"
|
|
10
|
+
require "pathname"
|
|
11
|
+
require "json"
|
|
12
|
+
|
|
13
|
+
require_relative "example_type"
|
|
14
|
+
require_relative "task_helpers"
|
|
15
|
+
|
|
16
|
+
namespace :shakapacker_examples do # rubocop:disable Metrics/BlockLength
|
|
17
|
+
include ReactOnRails::TaskHelpers
|
|
18
|
+
|
|
19
|
+
# Updates React-related dependencies to a specific version
|
|
20
|
+
def update_react_dependencies(deps, react_version)
|
|
21
|
+
return unless deps
|
|
22
|
+
|
|
23
|
+
deps["react"] = react_version
|
|
24
|
+
deps["react-dom"] = react_version
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Updates Shakapacker to minimum supported version in either dependencies or devDependencies
|
|
28
|
+
def update_shakapacker_dependency(deps, dev_deps)
|
|
29
|
+
if dev_deps&.key?("shakapacker")
|
|
30
|
+
dev_deps["shakapacker"] = ExampleType::MINIMUM_SHAKAPACKER_VERSION
|
|
31
|
+
elsif deps&.key?("shakapacker")
|
|
32
|
+
deps["shakapacker"] = ExampleType::MINIMUM_SHAKAPACKER_VERSION
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Updates dependencies in package.json to use specific React version
|
|
37
|
+
def update_package_json_for_react_version(package_json_path, react_version)
|
|
38
|
+
return unless File.exist?(package_json_path)
|
|
39
|
+
|
|
40
|
+
begin
|
|
41
|
+
package_json = JSON.parse(File.read(package_json_path))
|
|
42
|
+
rescue JSON::ParserError => e
|
|
43
|
+
puts " ERROR: Failed to parse #{package_json_path}: #{e.message}"
|
|
44
|
+
raise
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
deps = package_json["dependencies"]
|
|
48
|
+
dev_deps = package_json["devDependencies"]
|
|
49
|
+
|
|
50
|
+
update_react_dependencies(deps, react_version)
|
|
51
|
+
# Shakapacker 8.2.0 requires webpack-assets-manifest ^5.x (v6.x uses ESM and breaks)
|
|
52
|
+
# Always add this explicitly since the transitive dependency from shakapacker may be v6.x
|
|
53
|
+
dev_deps["webpack-assets-manifest"] = "^5.0.6" if dev_deps
|
|
54
|
+
# Shakapacker 8.2.0 requires babel-loader to be explicitly installed as a devDependency
|
|
55
|
+
# (in 9.x this requirement was relaxed or the package structure changed)
|
|
56
|
+
dev_deps["babel-loader"] = "^9.1.3" if dev_deps
|
|
57
|
+
# @babel/plugin-transform-runtime is required by the default babel config but not
|
|
58
|
+
# automatically included as a dependency in older Shakapacker versions
|
|
59
|
+
dev_deps["@babel/plugin-transform-runtime"] = "^7.24.0" if dev_deps
|
|
60
|
+
update_shakapacker_dependency(deps, dev_deps)
|
|
61
|
+
|
|
62
|
+
# Add npm overrides to force specific React version, preventing yalc-linked
|
|
63
|
+
# react-on-rails from pulling in React 19 as a transitive dependency
|
|
64
|
+
package_json["overrides"] = {
|
|
65
|
+
"react" => react_version,
|
|
66
|
+
"react-dom" => react_version
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
File.write(package_json_path, "#{JSON.pretty_generate(package_json)}\n")
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Updates Gemfile to pin shakapacker to minimum version
|
|
73
|
+
# (must match the npm package version exactly)
|
|
74
|
+
def update_gemfile_versions(gemfile_path)
|
|
75
|
+
return unless File.exist?(gemfile_path)
|
|
76
|
+
|
|
77
|
+
gemfile_content = File.read(gemfile_path)
|
|
78
|
+
# Replace any shakapacker gem line with exact version pin
|
|
79
|
+
# Handle both single-line: gem 'shakapacker', '>= 8.2.0'
|
|
80
|
+
# And multi-line declarations:
|
|
81
|
+
# gem 'shakapacker',
|
|
82
|
+
# '>= 8.2.0'
|
|
83
|
+
gemfile_content = gemfile_content.gsub(
|
|
84
|
+
/gem ['"]shakapacker['"][^\n]*(?:\n\s+[^g\n][^\n]*)*$/m,
|
|
85
|
+
"gem 'shakapacker', '#{ExampleType::MINIMUM_SHAKAPACKER_VERSION}'"
|
|
86
|
+
)
|
|
87
|
+
File.write(gemfile_path, gemfile_content)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Updates package.json and Gemfile to use specific React version for compatibility testing
|
|
91
|
+
def apply_react_version(dir, react_version)
|
|
92
|
+
update_package_json_for_react_version(File.join(dir, "package.json"), react_version)
|
|
93
|
+
update_gemfile_versions(File.join(dir, "Gemfile"))
|
|
94
|
+
|
|
95
|
+
puts " Updated package.json for compatibility testing:"
|
|
96
|
+
puts " React: #{react_version}"
|
|
97
|
+
puts " Shakapacker: #{ExampleType::MINIMUM_SHAKAPACKER_VERSION}"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Define tasks for each example type
|
|
101
|
+
ExampleType.all[:shakapacker_examples].each do |example_type| # rubocop:disable Metrics/BlockLength
|
|
102
|
+
relative_gem_root = Pathname(gem_root).relative_path_from(Pathname(example_type.dir))
|
|
103
|
+
# CLOBBER
|
|
104
|
+
desc "Clobbers (deletes) #{example_type.name_pretty}"
|
|
105
|
+
task example_type.clobber_task_name_short do
|
|
106
|
+
rm_rf(example_type.dir)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# GENERATE
|
|
110
|
+
desc "Generates #{example_type.name_pretty}"
|
|
111
|
+
task example_type.gen_task_name_short => example_type.clobber_task_name do
|
|
112
|
+
puts "Running shakapacker_examples:#{example_type.gen_task_name_short}"
|
|
113
|
+
mkdir_p(example_type.dir)
|
|
114
|
+
sh_in_dir(examples_dir, "rails new #{example_type.name} #{example_type.rails_options} --skip-javascript")
|
|
115
|
+
sh_in_dir(example_type.dir, "touch .gitignore")
|
|
116
|
+
sh_in_dir(example_type.dir,
|
|
117
|
+
"echo \"gem 'react_on_rails', path: '#{relative_gem_root}'\" >> #{example_type.gemfile}")
|
|
118
|
+
# Pin to 9.4.0 to ensure gem and npm package versions match
|
|
119
|
+
# (shakapacker 9.5.0 gem was released but npm package version detection has issues)
|
|
120
|
+
sh_in_dir(example_type.dir, "echo \"gem 'shakapacker', '9.4.0'\" >> #{example_type.gemfile}")
|
|
121
|
+
bundle_install_in(example_type.dir)
|
|
122
|
+
sh_in_dir(example_type.dir, "rake shakapacker:install")
|
|
123
|
+
# Skip validation when running generators on example apps during development.
|
|
124
|
+
# The generator validates that certain config options exist in the initializer,
|
|
125
|
+
# but during example generation, we're often testing against the current gem
|
|
126
|
+
# codebase which may have new config options not yet in the released version.
|
|
127
|
+
# This allows examples to be generated without validation errors while still
|
|
128
|
+
# testing the generator functionality.
|
|
129
|
+
generator_commands = example_type.generator_shell_commands.map do |cmd|
|
|
130
|
+
"REACT_ON_RAILS_SKIP_VALIDATION=true #{cmd}"
|
|
131
|
+
end
|
|
132
|
+
sh_in_dir(example_type.dir, generator_commands)
|
|
133
|
+
# Re-run bundle install since dev_tests generator adds rspec-rails and coveralls to Gemfile
|
|
134
|
+
bundle_install_in(example_type.dir)
|
|
135
|
+
|
|
136
|
+
# Apply specific React version for compatibility testing examples
|
|
137
|
+
if example_type.pinned_react_version?
|
|
138
|
+
apply_react_version(example_type.dir, example_type.react_version_string)
|
|
139
|
+
# Re-run bundle install since Gemfile was updated with pinned shakapacker version
|
|
140
|
+
bundle_install_in(example_type.dir)
|
|
141
|
+
# Run npm install BEFORE shakapacker:binstubs to ensure the npm shakapacker version
|
|
142
|
+
# matches the gem version. The binstubs task loads the Rails environment which
|
|
143
|
+
# validates version matching between gem and npm package.
|
|
144
|
+
# Use --legacy-peer-deps to avoid peer dependency conflicts when yalc-linked
|
|
145
|
+
# react-on-rails expects newer React versions
|
|
146
|
+
sh_in_dir(example_type.dir, "npm install --legacy-peer-deps")
|
|
147
|
+
# Regenerate Shakapacker binstubs after downgrading from 9.x to 8.2.x
|
|
148
|
+
# The binstub format may differ between major versions
|
|
149
|
+
unbundled_sh_in_dir(example_type.dir, "bundle exec rake shakapacker:binstubs")
|
|
150
|
+
else
|
|
151
|
+
sh_in_dir(example_type.dir, "npm install")
|
|
152
|
+
end
|
|
153
|
+
# Generate the component packs after running the generator to ensure all
|
|
154
|
+
# auto-bundled components have corresponding pack files created.
|
|
155
|
+
# Use unbundled_sh_in_dir to ensure we're using the generated app's Gemfile
|
|
156
|
+
# and gem versions, not the parent workspace's bundle context.
|
|
157
|
+
unbundled_sh_in_dir(example_type.dir, "bundle exec rake react_on_rails:generate_packs")
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
desc "Clobbers (deletes) all example apps"
|
|
162
|
+
task :clobber do
|
|
163
|
+
rm_rf(examples_dir)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
desc "Generates all example apps"
|
|
167
|
+
task gen_all: ExampleType.all[:shakapacker_examples].map(&:gen_task_name)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
desc "Generates all example apps. Run `rake -D examples` to see all available options"
|
|
171
|
+
task shakapacker_examples: ["shakapacker_examples:gen_all"]
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "English"
|
|
4
|
+
|
|
5
|
+
module ReactOnRails
|
|
6
|
+
module TaskHelpers
|
|
7
|
+
# Returns the root folder of the monorepo
|
|
8
|
+
def monorepo_root
|
|
9
|
+
File.expand_path("../..", __dir__)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Returns the root folder of the react_on_rails gem
|
|
13
|
+
def gem_root
|
|
14
|
+
File.join(monorepo_root, "react_on_rails")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Returns the folder where examples are located
|
|
18
|
+
def examples_dir
|
|
19
|
+
File.join(monorepo_root, "gen-examples", "examples")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def dummy_app_dir
|
|
23
|
+
File.join(gem_root, "spec/dummy")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Executes a string or an array of strings in a shell in the given directory in an unbundled environment
|
|
27
|
+
def sh_in_dir(dir, *shell_commands)
|
|
28
|
+
shell_commands.flatten.each { |shell_command| sh %(cd #{dir} && #{shell_command.strip}) }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Executes a string or an array of strings in a shell in the given directory
|
|
32
|
+
def unbundled_sh_in_dir(dir, *shell_commands)
|
|
33
|
+
Dir.chdir(dir) do
|
|
34
|
+
# Without `with_unbundled_env`, running bundle in the child directories won't correctly
|
|
35
|
+
# update the Gemfile.lock
|
|
36
|
+
Bundler.with_unbundled_env do
|
|
37
|
+
shell_commands.flatten.each do |shell_command|
|
|
38
|
+
sh(shell_command.strip)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def bundle_install_in(dir)
|
|
45
|
+
required_version = detect_bundler_ruby_version(dir)
|
|
46
|
+
|
|
47
|
+
if required_version && required_version != RUBY_VERSION
|
|
48
|
+
puts " Switching Ruby version: #{RUBY_VERSION} → #{required_version}"
|
|
49
|
+
# Run version switch and bundle install in the same shell context
|
|
50
|
+
bundle_install_with_ruby_version(dir, required_version)
|
|
51
|
+
else
|
|
52
|
+
unbundled_sh_in_dir(dir, "bundle install")
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
# Runs bundle install with the specified Ruby version in the same shell context
|
|
59
|
+
def bundle_install_with_ruby_version(dir, version)
|
|
60
|
+
version_manager = ENV.fetch("RUBY_VERSION_MANAGER", "rvm")
|
|
61
|
+
|
|
62
|
+
command = case version_manager
|
|
63
|
+
when "rvm"
|
|
64
|
+
"rvm #{version} do bundle install"
|
|
65
|
+
when "rbenv"
|
|
66
|
+
"RBENV_VERSION=#{version} bundle install"
|
|
67
|
+
when "asdf"
|
|
68
|
+
"asdf shell ruby #{version} && bundle install"
|
|
69
|
+
else
|
|
70
|
+
# TODO: add support for chruby
|
|
71
|
+
puts " ⚠️ Unknown RUBY_VERSION_MANAGER: #{version_manager}"
|
|
72
|
+
puts " Supported values: rvm, rbenv, asdf"
|
|
73
|
+
raise "Ruby version #{version} required. Current: #{RUBY_VERSION}"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
unbundled_sh_in_dir(dir, command)
|
|
77
|
+
rescue StandardError => e
|
|
78
|
+
puts " ⚠️ Failed to switch Ruby version and run bundle install: #{e.message}"
|
|
79
|
+
puts " Please manually switch to Ruby #{version} and try again"
|
|
80
|
+
raise
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Detects the required Ruby version using Bundler
|
|
84
|
+
def detect_bundler_ruby_version(dir)
|
|
85
|
+
output = nil
|
|
86
|
+
exit_status = nil
|
|
87
|
+
|
|
88
|
+
# Run in unbundled environment to avoid conflicts with parent Bundler context
|
|
89
|
+
Bundler.with_unbundled_env do
|
|
90
|
+
Dir.chdir(dir) do
|
|
91
|
+
output = `bundle platform --ruby 2>&1`
|
|
92
|
+
exit_status = $CHILD_STATUS.exitstatus
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
unless exit_status.zero?
|
|
97
|
+
puts " ⚠️ Failed to detect Ruby version in #{dir}"
|
|
98
|
+
puts " Error: #{output.strip}" unless output.strip.empty?
|
|
99
|
+
return nil
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Parse "ruby 3.3.7" or "ruby 3.3.7-rc1" or "ruby 3.4.0-preview1"
|
|
103
|
+
# Regex matches: digits.dots followed by optional -prerelease
|
|
104
|
+
match = output.strip.match(/ruby\s+([\d.]+(?:-[a-zA-Z0-9.]+)?)/)
|
|
105
|
+
match ? match[1] : nil
|
|
106
|
+
rescue StandardError => e
|
|
107
|
+
puts " ⚠️ Error detecting Ruby version: #{e.message}"
|
|
108
|
+
nil
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
public
|
|
112
|
+
|
|
113
|
+
def bundle_install_in_no_turbolinks(dir)
|
|
114
|
+
sh_in_dir(dir, "DISABLE_TURBOLINKS=TRUE bundle install")
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Runs bundle exec using that directory's Gemfile
|
|
118
|
+
def bundle_exec(dir: nil, args: nil, env_vars: "")
|
|
119
|
+
sh_in_dir(dir, "#{env_vars} bundle exec #{args}")
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def generators_source_dir
|
|
123
|
+
File.join(gem_root, "lib/generators/react_on_rails")
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def symbolize_keys(hash)
|
|
127
|
+
hash.each_with_object({}) do |(key, value), new_hash|
|
|
128
|
+
new_key = key.is_a?(String) ? key.to_sym : key
|
|
129
|
+
new_value = value.is_a?(Hash) ? symbolize_keys(value) : value
|
|
130
|
+
new_hash[new_key] = new_value
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "English"
|
|
4
|
+
require "bundler"
|
|
5
|
+
require_relative "task_helpers"
|
|
6
|
+
|
|
7
|
+
CLAUDE_CODE_TIP = <<~TIP
|
|
8
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
9
|
+
│ TIP: This task only adds version headers and links, not changelog entries. │
|
|
10
|
+
│ For full automation, run /update-changelog in Claude Code. │
|
|
11
|
+
│ │
|
|
12
|
+
│ After running this task, manually add entries under the new header: │
|
|
13
|
+
│ #### Fixed / #### Added / #### Changed / etc. │
|
|
14
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
15
|
+
TIP
|
|
16
|
+
|
|
17
|
+
# Update the compare links at the bottom of the changelog
|
|
18
|
+
# version: version string without 'v' prefix (e.g., "16.2.0.beta.20")
|
|
19
|
+
# anchor: markdown anchor (e.g., "[16.2.0.beta.20]")
|
|
20
|
+
def update_changelog_links(changelog, version, anchor)
|
|
21
|
+
compare_link_prefix = "https://github.com/shakacode/react_on_rails/compare"
|
|
22
|
+
match_data = %r{#{compare_link_prefix}/(?<prev_version>.*)\.\.\.master}.match(changelog)
|
|
23
|
+
return unless match_data
|
|
24
|
+
|
|
25
|
+
prev_version = match_data[:prev_version]
|
|
26
|
+
new_unreleased_link = "#{compare_link_prefix}/#{version}...master"
|
|
27
|
+
new_version_link = "#{anchor}: #{compare_link_prefix}/#{prev_version}...#{version}"
|
|
28
|
+
changelog.sub!(match_data[0], "#{new_unreleased_link}\n#{new_version_link}")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Insert version header into changelog, returns true if successful
|
|
32
|
+
def insert_version_header(changelog, anchor, tag_date)
|
|
33
|
+
# Try inserting right after ### [Unreleased] first
|
|
34
|
+
return true if changelog.sub!("### [Unreleased]", "### [Unreleased]\n\n### #{anchor} - #{tag_date}")
|
|
35
|
+
|
|
36
|
+
# Fallback: insert after "Changes since the last non-beta release."
|
|
37
|
+
return true if changelog.sub!("Changes since the last non-beta release.", "\\0\n\n### #{anchor} - #{tag_date}")
|
|
38
|
+
|
|
39
|
+
false
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
desc "Updates CHANGELOG.md inserting headers for the new version (headers only, not content).
|
|
43
|
+
Argument: Git tag. Defaults to the latest tag.
|
|
44
|
+
TIP: Use /update-changelog in Claude Code for full automation."
|
|
45
|
+
|
|
46
|
+
task :update_changelog, %i[tag] do |_, args|
|
|
47
|
+
puts CLAUDE_CODE_TIP
|
|
48
|
+
|
|
49
|
+
# Git tags use 'v' prefix (e.g., v16.2.0), but CHANGELOG uses versions without it
|
|
50
|
+
git_tag = args[:tag] || `git describe --tags --abbrev=0`.strip
|
|
51
|
+
changelog_version = git_tag.delete_prefix("v")
|
|
52
|
+
anchor = "[#{changelog_version}]"
|
|
53
|
+
changelog = File.read("CHANGELOG.md")
|
|
54
|
+
|
|
55
|
+
if changelog.include?(anchor)
|
|
56
|
+
puts "Tag #{git_tag} is already documented in CHANGELOG.md"
|
|
57
|
+
next
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
tag_date = `git show -s --format=%cs #{git_tag} 2>&1`.split("\n").last&.strip
|
|
61
|
+
abort("Failed to find tag #{git_tag}") unless $CHILD_STATUS.success? && tag_date
|
|
62
|
+
|
|
63
|
+
unless insert_version_header(changelog, anchor, tag_date)
|
|
64
|
+
abort("Failed to insert version header: could not find '### [Unreleased]' " \
|
|
65
|
+
"or 'Changes since the last non-beta release.' in CHANGELOG.md")
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
update_changelog_links(changelog, changelog_version, anchor)
|
|
69
|
+
|
|
70
|
+
File.write("CHANGELOG.md", changelog)
|
|
71
|
+
puts "Updated CHANGELOG.md with version header for #{git_tag}"
|
|
72
|
+
puts "NOTE: You still need to write the changelog entries manually."
|
|
73
|
+
end
|
data/react_on_rails.gemspec
CHANGED
|
@@ -16,9 +16,10 @@ Gem::Specification.new do |s|
|
|
|
16
16
|
s.homepage = "https://github.com/shakacode/react_on_rails"
|
|
17
17
|
s.license = "MIT"
|
|
18
18
|
|
|
19
|
-
s.files =
|
|
20
|
-
|
|
21
|
-
f.match(%r{^(
|
|
19
|
+
s.files = Dir.chdir(__dir__) do
|
|
20
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
21
|
+
f.match(%r{^(spec|tmp)/})
|
|
22
|
+
end
|
|
22
23
|
end
|
|
23
24
|
s.bindir = "exe"
|
|
24
25
|
s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
data/sig/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# RBS Type Signatures
|
|
2
|
+
|
|
3
|
+
This directory contains RBS (Ruby Signature) type definitions for the React on Rails gem.
|
|
4
|
+
|
|
5
|
+
## What is RBS?
|
|
6
|
+
|
|
7
|
+
RBS is Ruby's official type signature language. It provides type information for Ruby code, enabling:
|
|
8
|
+
|
|
9
|
+
- Better IDE support and autocomplete
|
|
10
|
+
- Static type checking with tools like Steep
|
|
11
|
+
- Improved documentation
|
|
12
|
+
- Early detection of type-related bugs
|
|
13
|
+
|
|
14
|
+
## Structure
|
|
15
|
+
|
|
16
|
+
The signatures are organized to mirror the `lib/` directory structure:
|
|
17
|
+
|
|
18
|
+
- `react_on_rails.rbs` - Main module and core classes
|
|
19
|
+
- `react_on_rails/configuration.rbs` - Configuration class types
|
|
20
|
+
- `react_on_rails/helper.rbs` - View helper method signatures
|
|
21
|
+
- `react_on_rails/server_rendering_pool.rbs` - Server rendering types
|
|
22
|
+
- `react_on_rails/utils.rbs` - Utility method signatures
|
|
23
|
+
- And more...
|
|
24
|
+
|
|
25
|
+
## Validation
|
|
26
|
+
|
|
27
|
+
To validate the RBS signatures:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
bundle exec rake rbs:validate
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Or directly:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
bundle exec rbs -I sig validate
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
To list all RBS files:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
bundle exec rake rbs:list
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Development
|
|
46
|
+
|
|
47
|
+
When adding new public methods or classes to the gem, please also add corresponding RBS signatures.
|
|
48
|
+
|
|
49
|
+
For more information about RBS:
|
|
50
|
+
|
|
51
|
+
- [RBS Documentation](https://github.com/ruby/rbs)
|
|
52
|
+
- [RBS Syntax Guide](https://github.com/ruby/rbs/blob/master/docs/syntax.md)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
module ReactOnRails
|
|
2
|
+
class Configuration
|
|
3
|
+
attr_accessor node_modules_location: String?
|
|
4
|
+
attr_accessor server_bundle_js_file: String
|
|
5
|
+
attr_accessor prerender: bool
|
|
6
|
+
attr_accessor replay_console: bool
|
|
7
|
+
attr_accessor trace: bool
|
|
8
|
+
attr_accessor development_mode: bool
|
|
9
|
+
attr_accessor logging_on_server: bool
|
|
10
|
+
attr_accessor server_renderer_pool_size: Integer
|
|
11
|
+
attr_accessor server_renderer_timeout: Integer
|
|
12
|
+
attr_accessor skip_display_none: bool?
|
|
13
|
+
attr_accessor raise_on_prerender_error: bool
|
|
14
|
+
attr_accessor generated_assets_dirs: Array[String]?
|
|
15
|
+
attr_accessor generated_assets_dir: String
|
|
16
|
+
attr_accessor components_subdirectory: String?
|
|
17
|
+
attr_accessor webpack_generated_files: Array[String]
|
|
18
|
+
attr_accessor rendering_extension: String?
|
|
19
|
+
attr_accessor build_test_command: String
|
|
20
|
+
attr_accessor build_production_command: String
|
|
21
|
+
attr_accessor i18n_dir: String?
|
|
22
|
+
attr_accessor i18n_yml_dir: String?
|
|
23
|
+
attr_accessor i18n_output_format: Symbol?
|
|
24
|
+
attr_accessor i18n_yml_safe_load_options: Hash[Symbol, untyped]?
|
|
25
|
+
attr_accessor defer_generated_component_packs: bool
|
|
26
|
+
attr_accessor server_render_method: String?
|
|
27
|
+
attr_accessor random_dom_id: bool
|
|
28
|
+
attr_accessor auto_load_bundle: bool
|
|
29
|
+
attr_accessor same_bundle_for_client_and_server: bool
|
|
30
|
+
attr_accessor rendering_props_extension: String?
|
|
31
|
+
attr_accessor make_generated_server_bundle_the_entrypoint: bool
|
|
32
|
+
attr_accessor generated_component_packs_loading_strategy: Symbol?
|
|
33
|
+
attr_accessor immediate_hydration: bool
|
|
34
|
+
attr_accessor component_registry_timeout: Integer
|
|
35
|
+
attr_accessor server_bundle_output_path: String?
|
|
36
|
+
attr_accessor enforce_private_server_bundles: bool
|
|
37
|
+
|
|
38
|
+
def initialize: (
|
|
39
|
+
?node_modules_location: String?,
|
|
40
|
+
?server_bundle_js_file: String?,
|
|
41
|
+
?prerender: bool?,
|
|
42
|
+
?replay_console: bool?,
|
|
43
|
+
?make_generated_server_bundle_the_entrypoint: bool?,
|
|
44
|
+
?trace: bool?,
|
|
45
|
+
?development_mode: bool?,
|
|
46
|
+
?defer_generated_component_packs: bool?,
|
|
47
|
+
?logging_on_server: bool?,
|
|
48
|
+
?server_renderer_pool_size: Integer?,
|
|
49
|
+
?server_renderer_timeout: Integer?,
|
|
50
|
+
?raise_on_prerender_error: bool?,
|
|
51
|
+
?skip_display_none: bool?,
|
|
52
|
+
?generated_assets_dirs: Array[String]?,
|
|
53
|
+
?generated_assets_dir: String?,
|
|
54
|
+
?webpack_generated_files: Array[String]?,
|
|
55
|
+
?rendering_extension: String?,
|
|
56
|
+
?build_test_command: String?,
|
|
57
|
+
?build_production_command: String?,
|
|
58
|
+
?generated_component_packs_loading_strategy: Symbol?,
|
|
59
|
+
?same_bundle_for_client_and_server: bool?,
|
|
60
|
+
?i18n_dir: String?,
|
|
61
|
+
?i18n_yml_dir: String?,
|
|
62
|
+
?i18n_output_format: Symbol?,
|
|
63
|
+
?i18n_yml_safe_load_options: Hash[Symbol, untyped]?,
|
|
64
|
+
?random_dom_id: bool?,
|
|
65
|
+
?server_render_method: String?,
|
|
66
|
+
?rendering_props_extension: String?,
|
|
67
|
+
?components_subdirectory: String?,
|
|
68
|
+
?auto_load_bundle: bool?,
|
|
69
|
+
?immediate_hydration: bool?,
|
|
70
|
+
?component_registry_timeout: Integer?,
|
|
71
|
+
?server_bundle_output_path: String?,
|
|
72
|
+
?enforce_private_server_bundles: bool?
|
|
73
|
+
) -> void
|
|
74
|
+
|
|
75
|
+
def setup_config_values: () -> void
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def check_component_registry_timeout: () -> void
|
|
80
|
+
def validate_generated_component_packs_loading_strategy: () -> void
|
|
81
|
+
def validate_enforce_private_server_bundles: () -> void
|
|
82
|
+
def check_minimum_shakapacker_version: () -> void
|
|
83
|
+
def check_autobundling_requirements: () -> void
|
|
84
|
+
def adjust_precompile_task: () -> void
|
|
85
|
+
def error_if_using_packer_and_generated_assets_dir_not_match_public_output_path: () -> void
|
|
86
|
+
def check_server_render_method_is_only_execjs: () -> void
|
|
87
|
+
def configure_generated_assets_dirs_deprecation: () -> void
|
|
88
|
+
def ensure_webpack_generated_files_exists: () -> void
|
|
89
|
+
def configure_skip_display_none_deprecation: () -> void
|
|
90
|
+
def raise_missing_components_subdirectory: () -> void
|
|
91
|
+
def compile_command_conflict_message: () -> String
|
|
92
|
+
def rsc_bundle_js_file: () -> String?
|
|
93
|
+
def react_client_manifest_file: () -> String?
|
|
94
|
+
def react_server_client_manifest_file: () -> String?
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module ReactOnRails
|
|
2
|
+
module Controller
|
|
3
|
+
# Type alias for ActiveSupport::SafeBuffer (html_safe strings)
|
|
4
|
+
type safe_buffer = String
|
|
5
|
+
|
|
6
|
+
def redux_store: (
|
|
7
|
+
String store_name,
|
|
8
|
+
?props: Hash[Symbol, untyped] | String,
|
|
9
|
+
?immediate_hydration: bool
|
|
10
|
+
) -> void
|
|
11
|
+
|
|
12
|
+
# Returns html_safe string (ActiveSupport::SafeBuffer)
|
|
13
|
+
def redux_store_hydration_data: () -> safe_buffer
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module ReactOnRails
|
|
2
|
+
module Dev
|
|
3
|
+
class FileManager
|
|
4
|
+
def self.cleanup_stale_files: () -> bool
|
|
5
|
+
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def self.cleanup_overmind_sockets: () -> bool
|
|
9
|
+
def self.cleanup_rails_pid_file: () -> bool
|
|
10
|
+
def self.overmind_running?: () -> bool
|
|
11
|
+
def self.process_running?: (Integer) -> bool
|
|
12
|
+
def self.remove_file_if_exists: (String, String) -> bool
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module ReactOnRails
|
|
2
|
+
module Dev
|
|
3
|
+
class PackGenerator
|
|
4
|
+
def self.generate: (?verbose: bool) -> void
|
|
5
|
+
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def self.run_pack_generation: (?silent: bool, ?verbose: bool) -> bool
|
|
9
|
+
def self.should_run_directly?: () -> bool
|
|
10
|
+
def self.rails_available?: () -> bool
|
|
11
|
+
def self.run_rake_task_directly: (?silent: bool) -> bool
|
|
12
|
+
def self.load_rake_tasks: () -> void
|
|
13
|
+
def self.prepare_rake_task: () -> untyped
|
|
14
|
+
def self.capture_output: (bool) { () -> bool } -> bool
|
|
15
|
+
def self.handle_rake_error: (Exception, bool) -> void
|
|
16
|
+
def self.run_via_bundle_exec: (?silent: bool, ?verbose: bool) -> (bool | nil)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module ReactOnRails
|
|
2
|
+
module Dev
|
|
3
|
+
class ProcessManager
|
|
4
|
+
VERSION_CHECK_TIMEOUT: Integer
|
|
5
|
+
|
|
6
|
+
def self.installed?: (String) -> bool
|
|
7
|
+
def self.ensure_procfile: (String) -> void
|
|
8
|
+
def self.run_with_process_manager: (String) -> void
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def self.installed_in_current_context?: (String) -> bool
|
|
13
|
+
def self.version_flags_for: (String) -> Array[String]
|
|
14
|
+
def self.run_process_if_available: (String, Array[String]) -> (bool | nil)
|
|
15
|
+
def self.run_process_outside_bundle: (String, Array[String]) -> bool
|
|
16
|
+
def self.process_available_in_system?: (String) -> bool
|
|
17
|
+
def self.with_unbundled_context: () { () -> untyped } -> untyped
|
|
18
|
+
def self.show_process_manager_installation_help: () -> void
|
|
19
|
+
def self.valid_procfile_path?: (String) -> bool
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module ReactOnRails
|
|
2
|
+
module Dev
|
|
3
|
+
class ServerManager
|
|
4
|
+
type mode = :development | :production_like | :static | :hmr
|
|
5
|
+
|
|
6
|
+
def self.start: (mode, String?, ?verbose: bool, ?route: String?, ?rails_env: String?) -> void
|
|
7
|
+
def self.kill_processes: () -> void
|
|
8
|
+
def self.development_processes: () -> Hash[String, String]
|
|
9
|
+
def self.kill_running_processes: () -> bool
|
|
10
|
+
def self.find_process_pids: (String) -> Array[Integer]
|
|
11
|
+
def self.terminate_processes: (Array[Integer]) -> void
|
|
12
|
+
def self.kill_port_processes: (Array[Integer]) -> bool
|
|
13
|
+
def self.find_port_pids: (Integer) -> Array[Integer]
|
|
14
|
+
def self.cleanup_socket_files: () -> bool
|
|
15
|
+
def self.print_kill_summary: (bool) -> void
|
|
16
|
+
def self.show_help: () -> void
|
|
17
|
+
def self.run_from_command_line: (?Array[String]) -> void
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def self.help_usage: () -> String
|
|
22
|
+
def self.help_commands: () -> String
|
|
23
|
+
def self.help_options: () -> String
|
|
24
|
+
def self.help_customization: () -> String
|
|
25
|
+
def self.help_mode_details: () -> String
|
|
26
|
+
def self.help_troubleshooting: () -> String
|
|
27
|
+
def self.run_production_like: (?_verbose: bool, ?route: String?, ?rails_env: String?) -> void
|
|
28
|
+
def self.run_static_development: (String, ?verbose: bool, ?route: String?) -> void
|
|
29
|
+
def self.run_development: (String, ?verbose: bool, ?route: String?) -> void
|
|
30
|
+
def self.print_server_info: (String, Array[String], ?Integer, ?route: String?) -> void
|
|
31
|
+
def self.print_procfile_info: (String, ?route: String?) -> void
|
|
32
|
+
def self.procfile_port: (String) -> Integer
|
|
33
|
+
def self.box_border: (Integer) -> String
|
|
34
|
+
def self.box_bottom: (Integer) -> String
|
|
35
|
+
def self.box_empty_line: (Integer) -> String
|
|
36
|
+
def self.format_box_line: (String, Integer) -> String
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|