Module: GeneratorHelper
- Included in:
- ReactOnRails::Generators::BaseGenerator, ReactOnRails::Generators::DevTestsGenerator, ReactOnRails::Generators::InstallGenerator, ReactOnRails::Generators::ProGenerator, ReactOnRails::Generators::ReactNoReduxGenerator, ReactOnRails::Generators::ReactWithReduxGenerator, ReactOnRails::Generators::RscGenerator
- Defined in:
- lib/generators/react_on_rails/generator_helper.rb
Overview
rubocop:disable Metrics/ModuleLength
Instance Method Summary collapse
- #add_documentation_reference(message, source) ⇒ Object
-
#add_npm_dependencies(packages, dev: false) ⇒ Object
Safe wrapper for package_json operations.
- #component_extension(options) ⇒ Object
- #copy_file_and_missing_parent_directories(source_file, destination_file = nil) ⇒ Object
- #dest_dir_exists?(dir) ⇒ Boolean
-
#dest_file_exists?(file) ⇒ Boolean
Takes a relative path from the destination root, such as ‘.gitignore` or `app/assets/javascripts/application.js`.
-
#destination_config_path(path) ⇒ String
Remap a config path from config/webpack/ to config/rspack/ when using rspack.
-
#detect_react_version ⇒ String?
Detect the installed React version from package.json Uses VERSION_PARTS_REGEX pattern from VersionChecker for consistency.
- #empty_directory_with_keep_file(destination, config = {}) ⇒ Object
-
#gem_in_lockfile?(gem_name) ⇒ Boolean
Check if a gem is present in Gemfile.lock Always checks the target app’s Gemfile.lock, not inherited BUNDLE_GEMFILE See: github.com/shakacode/react_on_rails/issues/2287.
- #keep_file(destination) ⇒ Object
- #mark_pro_gem_installed! ⇒ Object
- #package_json ⇒ Object
- #print_generator_messages ⇒ Object
-
#pro_gem_installed? ⇒ Boolean
Check if React on Rails Pro gem is installed.
-
#resolve_server_client_or_both_path ⇒ String?
Resolve the path to ServerClientOrBoth.js, handling the legacy name.
-
#root_route_present?(routes_path = File.join(destination_root, "config/routes.rb")) ⇒ Boolean
Detect whether config/routes.rb defines any non-commented root route.
- #setup_file_error(file, data) ⇒ Object
-
#shakapacker_version_9_or_higher? ⇒ Boolean
Check if Shakapacker 9.0 or higher is available Returns true if Shakapacker >= 9.0, false otherwise.
-
#symlink_dest_file_to_dest_file(target, link) ⇒ Object
As opposed to Rails::Generators::Testing.create_link, which creates a link pointing to source_root, this symlinks a file in destination_root to a file also in destination_root.
-
#use_pro? ⇒ Boolean
Check if Pro features should be enabled.
-
#use_rsc? ⇒ Boolean
Check if RSC (React Server Components) should be enabled Returns true if –rsc or –rsc-pro is explicitly set.
-
#use_rsc_pro_mode? ⇒ Boolean
Check if first-class RSC Pro mode should be enabled.
-
#using_rspack? ⇒ Boolean
Determine if the project is using rspack as the bundler.
-
#using_swc? ⇒ Boolean
Check if SWC is configured as the JavaScript transpiler in shakapacker.yml.
Instance Method Details
#add_documentation_reference(message, source) ⇒ Object
108 109 110 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 108 def add_documentation_reference(, source) "#{} \n#{source}" end |
#add_npm_dependencies(packages, dev: false) ⇒ Object
Safe wrapper for package_json operations
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 27 def add_npm_dependencies(packages, dev: false) pj = package_json return false unless pj begin result = if dev pj.manager.add(packages, type: :dev, exact: true) else pj.manager.add(packages, exact: true) end # package_json#add can return nil for successful side-effect operations. result != false rescue StandardError => e say_status :warning, "Could not add packages via package_json gem: #{e.}", :yellow say_status :warning, "Will fall back to direct npm commands.", :yellow false end end |
#component_extension(options) ⇒ Object
121 122 123 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 121 def component_extension() .typescript? ? "tsx" : "jsx" end |
#copy_file_and_missing_parent_directories(source_file, destination_file = nil) ⇒ Object
100 101 102 103 104 105 106 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 100 def copy_file_and_missing_parent_directories(source_file, destination_file = nil) destination_file ||= source_file destination_path = Pathname.new(destination_file) parent_directories = destination_path.dirname empty_directory(parent_directories) unless dest_dir_exists?(parent_directories) copy_file source_file, destination_file end |
#dest_dir_exists?(dir) ⇒ Boolean
52 53 54 55 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 52 def dest_dir_exists?(dir) dest_dir = File.join(destination_root, dir) Dir.exist?(dest_dir) ? dest_dir : nil end |
#dest_file_exists?(file) ⇒ Boolean
Takes a relative path from the destination root, such as ‘.gitignore` or `app/assets/javascripts/application.js`
47 48 49 50 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 47 def dest_file_exists?(file) dest_file = File.join(destination_root, file) File.exist?(dest_file) ? dest_file : nil end |
#destination_config_path(path) ⇒ String
Remap a config path from config/webpack/ to config/rspack/ when using rspack. Source templates always live under config/webpack/ (template names are stable); this method handles the destination remapping.
203 204 205 206 207 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 203 def destination_config_path(path) return path unless using_rspack? path.sub(%r{\Aconfig/webpack/}, "config/rspack/") end |
#detect_react_version ⇒ String?
Detect the installed React version from package.json Uses VERSION_PARTS_REGEX pattern from VersionChecker for consistency
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 213 def detect_react_version pj = package_json return nil unless pj dependencies = pj.fetch("dependencies", {}) react_version = dependencies["react"] return nil unless react_version # Skip non-version strings (workspace:*, file:, link:, http://, etc.) return nil if react_version.include?("/") || react_version.start_with?("workspace:") # Extract version using the same regex pattern as VersionChecker # Handles: "19.0.3", "^19.0.3", "~19.0.3", "19.0.3-beta.1", etc. match = react_version.match(/(\d+)\.(\d+)\.(\d+)(?:[-.]([0-9A-Za-z.-]+))?/) return nil unless match # Return the matched version (without pre-release suffix for comparison) "#{match[1]}.#{match[2]}.#{match[3]}" rescue StandardError nil end |
#empty_directory_with_keep_file(destination, config = {}) ⇒ Object
77 78 79 80 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 77 def empty_directory_with_keep_file(destination, config = {}) empty_directory(destination, config) keep_file(destination) end |
#gem_in_lockfile?(gem_name) ⇒ Boolean
Check if a gem is present in Gemfile.lock Always checks the target app’s Gemfile.lock, not inherited BUNDLE_GEMFILE See: github.com/shakacode/react_on_rails/issues/2287
131 132 133 134 135 136 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 131 def gem_in_lockfile?(gem_name) File.file?("Gemfile.lock") && File.foreach("Gemfile.lock").any? { |line| line.match?(/^\s{4}#{Regexp.escape(gem_name)}\s\(/) } rescue StandardError false end |
#keep_file(destination) ⇒ Object
82 83 84 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 82 def keep_file(destination) create_file("#{destination}/.keep") unless [:skip_keeps] end |
#mark_pro_gem_installed! ⇒ Object
151 152 153 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 151 def mark_pro_gem_installed! @pro_gem_installed = true end |
#package_json ⇒ Object
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 7 def package_json # Lazy load package_json gem only when actually needed for dependency management require "package_json" unless defined?(PackageJson) @package_json ||= PackageJson.read rescue LoadError unless @package_json_unavailable_warned say_status :warning, "package_json gem not available. This is expected before Shakapacker installation.", :yellow say_status :warning, "Dependencies will be installed using the default package manager after Shakapacker setup.", :yellow @package_json_unavailable_warned = true end nil rescue StandardError => e say_status :warning, "Could not read package.json: #{e.}", :yellow say_status :warning, "This is normal before Shakapacker creates the package.json file.", :yellow nil end |
#print_generator_messages ⇒ Object
112 113 114 115 116 117 118 119 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 112 def # GeneratorMessages stores pre-colored strings, so we strip ANSI manually for --no-color output. no_color = !shell.is_a?(Thor::Shell::Color) GeneratorMessages..each do || say(no_color ? .to_s.gsub(/\e\[[0-9;]*m/, "") : ) say "" # Blank line after each message for readability end end |
#pro_gem_installed? ⇒ Boolean
Check if React on Rails Pro gem is installed
Detection priority:
-
Gem.loaded_specs - gem is loaded in current Ruby process (most reliable)
-
Gemfile.lock - gem is resolved and installed
145 146 147 148 149 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 145 def pro_gem_installed? return @pro_gem_installed if defined?(@pro_gem_installed) @pro_gem_installed = Gem.loaded_specs.key?("react_on_rails_pro") || gem_in_lockfile?("react_on_rails_pro") end |
#resolve_server_client_or_both_path ⇒ String?
Resolve the path to ServerClientOrBoth.js, handling the legacy name. Old installs may still use generateWebpackConfigs.js; this renames it and updates references in environment configs so downstream transforms can rely on the canonical name.
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 291 def resolve_server_client_or_both_path new_path = destination_config_path("config/webpack/ServerClientOrBoth.js") old_path = destination_config_path("config/webpack/generateWebpackConfigs.js") full_new = File.join(destination_root, new_path) full_old = File.join(destination_root, old_path) if File.exist?(full_new) new_path elsif File.exist?(full_old) FileUtils.mv(full_old, full_new) %w[development.js production.js test.js].each do |env_file| env_path = destination_config_path("config/webpack/#{env_file}") if File.exist?(File.join(destination_root, env_path)) gsub_file(env_path, /generateWebpackConfigs/, "ServerClientOrBoth") end end new_path end end |
#root_route_present?(routes_path = File.join(destination_root, "config/routes.rb")) ⇒ Boolean
Detect whether config/routes.rb defines any non-commented root route.
61 62 63 64 65 66 67 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 61 def root_route_present?(routes_path = File.join(destination_root, "config/routes.rb")) return false unless File.file?(routes_path) File.foreach(routes_path).any? do |line| !line.match?(/^\s*#/) && line.match?(/^\s*root\b/) end end |
#setup_file_error(file, data) ⇒ Object
69 70 71 72 73 74 75 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 69 def setup_file_error(file, data) <<~MSG #{file} was not found. Please add the following content to your #{file} file: #{data} MSG end |
#shakapacker_version_9_or_higher? ⇒ Boolean
Default behavior: Returns true when Shakapacker is not yet installed Rationale: During fresh installations, we optimistically assume users will install the latest Shakapacker version. This ensures new projects get best-practice configs. If users later install an older version, the generated webpack config includes fallback logic (e.g., ‘config.privateOutputPath || hardcodedPath`) that prevents breakage, and validation warnings guide them to fix any misconfigurations.
Check if Shakapacker 9.0 or higher is available Returns true if Shakapacker >= 9.0, false otherwise
This method is used during code generation to determine which configuration patterns to use in generated files (e.g., config.privateOutputPath vs hardcoded paths).
249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 249 def shakapacker_version_9_or_higher? return @shakapacker_version_9_or_higher if defined?(@shakapacker_version_9_or_higher) @shakapacker_version_9_or_higher = begin # If Shakapacker is not available yet (fresh install), default to true # since we're likely installing the latest version return true unless defined?(ReactOnRails::PackerUtils) ReactOnRails::PackerUtils.shakapacker_version_requirement_met?("9.0.0") rescue StandardError # If we can't determine version, assume latest true end end |
#symlink_dest_file_to_dest_file(target, link) ⇒ Object
As opposed to Rails::Generators::Testing.create_link, which creates a link pointing to source_root, this symlinks a file in destination_root to a file also in destination_root.
89 90 91 92 93 94 95 96 97 98 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 89 def symlink_dest_file_to_dest_file(target, link) target_pathname = Pathname.new(File.join(destination_root, target)) link_pathname = Pathname.new(File.join(destination_root, link)) link_directory = link_pathname.dirname link_basename = link_pathname.basename target_relative_path = target_pathname.relative_path_from(link_directory) `cd #{link_directory} && ln -s #{target_relative_path} #{link_basename}` end |
#use_pro? ⇒ Boolean
Check if Pro features should be enabled. Returns true if –pro, –rsc, or –rsc-pro is set (RSC implies Pro).
167 168 169 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 167 def use_pro? [:pro] || [:rsc] || [:rsc_pro] end |
#use_rsc? ⇒ Boolean
Check if RSC (React Server Components) should be enabled Returns true if –rsc or –rsc-pro is explicitly set
175 176 177 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 175 def use_rsc? [:rsc] || [:rsc_pro] end |
#use_rsc_pro_mode? ⇒ Boolean
Check if first-class RSC Pro mode should be enabled. Returns true when –rsc-pro is set, or when users explicitly pass both –rsc and –pro.
159 160 161 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 159 def use_rsc_pro_mode? [:rsc_pro] || ([:rsc] && [:pro]) end |
#using_rspack? ⇒ Boolean
Determine if the project is using rspack as the bundler.
Detection priority:
-
Explicit –rspack option (most reliable during fresh installs)
-
config/shakapacker.yml assets_bundler setting (for standalone generators like ‘rails g react_on_rails:rsc` on an existing rspack project)
187 188 189 190 191 192 193 194 195 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 187 def using_rspack? return @using_rspack if defined?(@using_rspack) # options.key?(:rspack) is true when the generator declares --rspack (e.g. InstallGenerator), # false when it does not (e.g. RscGenerator, ProGenerator). Using .key? rather than .nil? # check on the value makes the intent explicit and avoids relying on Thor returning nil for # undeclared options. @using_rspack = .key?(:rspack) ? [:rspack] : rspack_configured_in_project? end |
#using_swc? ⇒ Boolean
This method is used to determine whether to install SWC dependencies (@swc/core, swc-loader) instead of Babel dependencies during generation.
Caching: The result is memoized for the lifetime of the generator instance. If shakapacker.yml changes during generator execution (unlikely), the cached value will not update. This is acceptable since generators run quickly.
Check if SWC is configured as the JavaScript transpiler in shakapacker.yml
Detection logic:
-
If shakapacker.yml exists and specifies javascript_transpiler: parse it
-
For Shakapacker 9.3.0+, SWC is the default if not specified
-
Returns true for fresh installations (SWC is recommended default)
279 280 281 282 283 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 279 def using_swc? return @using_swc if defined?(@using_swc) @using_swc = detect_swc_configuration end |