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.
-
#bundler_flag_given? ⇒ Boolean
True when the user passed any explicit bundler flag (–rspack/–no-rspack/–webpack/–no-webpack).
- #component_extension(options) ⇒ Object
-
#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.
-
#explicit_bundler_choice ⇒ Object
Resolve the explicit bundler flags into a single choice.
-
#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.
- #package_json ⇒ Object
- #print_generator_messages ⇒ Object
-
#pro_gem_installed? ⇒ Boolean
Check if React on Rails Pro gem is installed (real state — never “scheduled to be 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.
-
#rspack_bundler_default ⇒ Object
Bundler to use when no explicit bundler flag was passed.
-
#shakapacker_version_9_or_higher? ⇒ Boolean
Check if Shakapacker 9.0 or higher is available Returns true if Shakapacker >= 9.0, false otherwise.
-
#use_pro? ⇒ Boolean
Check if Pro features should be enabled.
-
#use_rsc? ⇒ Boolean
Check if RSC (React Server Components) 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
61 62 63 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 61 def add_documentation_reference(, source) "#{} \n#{source}" end |
#add_npm_dependencies(packages, dev: false) ⇒ Object
Safe wrapper for package_json operations
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 30 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 |
#bundler_flag_given? ⇒ Boolean
True when the user passed any explicit bundler flag (–rspack/–no-rspack/–webpack/–no-webpack).
176 177 178 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 176 def bundler_flag_given? .key?(:rspack) || .key?(:webpack) end |
#component_extension(options) ⇒ Object
74 75 76 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 74 def component_extension() .typescript? ? "tsx" : "jsx" 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.
194 195 196 197 198 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 194 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
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 204 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 |
#explicit_bundler_choice ⇒ Object
Resolve the explicit bundler flags into a single choice.
–rspack selects Rspack; –no-rspack and –webpack select Webpack (–webpack is a friendly alias for –no-rspack, and the auto-generated –no-webpack mirrors –rspack). Returns true for Rspack, false for Webpack, or nil when no bundler flag was passed (so the caller falls back to rspack_bundler_default).
IMPORTANT: this relies on Thor NOT including a nil-defaulted option in the hash when the flag is absent — options.key?(:rspack)/(:webpack) is true only when the user passed that flag. Re-adding ‘default:` to either class_option would make the key always present and break both the “no flag given” fallback and the conflict detection here. (Thor’s omit-when-no-default behavior verified against Thor 1.5.0; see Gemfile.lock.)
Passing contradictory flags (e.g. –rspack –webpack) raises a Thor::Error.
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 156 def explicit_bundler_choice choices = [] choices << [:rspack] if .key?(:rspack) # --webpack means "use Webpack" (rspack = false); --no-webpack means "use Rspack". # Name the inverted webpack flag so the rspack-boolean intent reads directly. rspack_via_webpack_flag = ![:webpack] choices << rspack_via_webpack_flag if .key?(:webpack) return nil if choices.empty? if choices.uniq.length > 1 raise Thor::Error, "Conflicting bundler flags: pass either Rspack (--rspack) or Webpack " \ "(--webpack / --no-rspack), not both." end choices.first 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
84 85 86 87 88 89 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 84 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 |
#package_json ⇒ Object
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 10 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
65 66 67 68 69 70 71 72 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 65 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 (real state — never “scheduled to be installed”).
Detection priority:
-
Gem.loaded_specs - gem is loaded in current Ruby process (most reliable)
-
Gemfile.lock - gem is resolved and installed
Use #pro_gem_install_deferred? for the broader “present, or will be installed by this generator run” meaning. Use #invalidate_pro_gem_installed_cache! after an operation that changes real state (e.g., bundle add) so the next call re-reads the lockfile.
102 103 104 105 106 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 102 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.
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 282 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.
53 54 55 56 57 58 59 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 53 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 |
#rspack_bundler_default ⇒ Object
Bundler to use when no explicit bundler flag was passed. Default (standalone generators like RscGenerator/ProGenerator): respect the existing project’s shakapacker.yml and never impose a bundler. InstallGenerator/BaseGenerator override this to default fresh installs to Rspack.
184 185 186 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 184 def rspack_bundler_default rspack_configured_in_project? 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).
240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 240 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 |
#use_pro? ⇒ Boolean
Check if Pro features should be enabled. Returns true if –pro or –rsc is set (RSC implies Pro).
112 113 114 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 112 def use_pro? [:pro] || [:rsc] end |
#use_rsc? ⇒ Boolean
Check if RSC (React Server Components) should be enabled. Returns true if –rsc is set.
120 121 122 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 120 def use_rsc? [:rsc] 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)
132 133 134 135 136 137 138 139 140 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 132 def using_rspack? return @using_rspack if defined?(@using_rspack) # An explicit bundler flag always wins. When none was passed (or the generator doesn't # declare the flags, e.g. RscGenerator/ProGenerator), fall back to the bundler default, # which each generator defines for its own context. explicit = explicit_bundler_choice @using_rspack = explicit.nil? ? rspack_bundler_default : explicit 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)
270 271 272 273 274 |
# File 'lib/generators/react_on_rails/generator_helper.rb', line 270 def using_swc? return @using_swc if defined?(@using_swc) @using_swc = detect_swc_configuration end |