Module: GeneratorHelper

Overview

rubocop:disable Metrics/ModuleLength

Instance Method Summary collapse

Instance Method Details

#absolute_generator_path(relative_path) ⇒ Object



274
275
276
# File 'lib/generators/react_on_rails/generator_helper.rb', line 274

def absolute_generator_path(relative_path)
  Pathname.new(File.join(destination_root, relative_path)).cleanpath
end

#absolute_path_relative_to_destination(pathname) ⇒ Object



267
268
269
270
271
272
# File 'lib/generators/react_on_rails/generator_helper.rb', line 267

def absolute_path_relative_to_destination(pathname)
  destination = Pathname.new(destination_root).cleanpath
  pathname.relative_path_from(destination).to_s
rescue ArgumentError
  nil # Signals the caller to fall back to the default path.
end

#add_documentation_reference(message, source) ⇒ Object



87
88
89
# File 'lib/generators/react_on_rails/generator_helper.rb', line 87

def add_documentation_reference(message, source)
  "#{message} \n#{source}"
end

#add_npm_dependencies(packages, dev: false) ⇒ Object

Safe wrapper for package_json operations



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/generators/react_on_rails/generator_helper.rb', line 56

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.message}", :yellow
    say_status :warning, "Will fall back to direct package manager commands.", :yellow
    false
  end
end

#bundler_flag_given?Boolean

True when the user passed any explicit bundler flag (--rspack/--no-rspack/--webpack/--no-webpack).

Returns:

  • (Boolean)


425
426
427
# File 'lib/generators/react_on_rails/generator_helper.rb', line 425

def bundler_flag_given?
  options.key?(:rspack) || options.key?(:webpack)
end

#component_extension(options) ⇒ Object



100
101
102
# File 'lib/generators/react_on_rails/generator_helper.rb', line 100

def component_extension(options)
  options.typescript? ? "tsx" : "jsx"
end

#configured_shakapacker_relative_path(config_key, default, allow_root: false) ⇒ Object



223
224
225
226
227
228
229
230
231
232
233
# File 'lib/generators/react_on_rails/generator_helper.rb', line 223

def configured_shakapacker_relative_path(config_key, default, allow_root: false)
  config_path = File.join(destination_root, "config/shakapacker.yml")
  return default unless File.exist?(config_path)

  config = parse_shakapacker_yml(config_path)
  configured_path = shakapacker_path_config_value(config, config_key)

  safe_generator_destination_path(configured_path, default:, allow_root:)
rescue Psych::SyntaxError
  default
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.

Parameters:

  • path (String)

    relative path, e.g. "config/webpack/serverWebpackConfig.js"

Returns:

  • (String)

    remapped path when rspack, unchanged otherwise



443
444
445
446
447
# File 'lib/generators/react_on_rails/generator_helper.rb', line 443

def destination_config_path(path)
  return path unless using_rspack?

  path.sub(%r{\Aconfig/webpack/}, "config/rspack/")
end

#detect_react_versionString?

Detect the installed React version from package.json Uses VERSION_PARTS_REGEX pattern from VersionChecker for consistency

Returns:

  • (String, nil)

    React version string (e.g., "19.0.3") or nil if not found/parseable



473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
# File 'lib/generators/react_on_rails/generator_helper.rb', line 473

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

#example_component_source_directory(component_name) ⇒ Object



214
215
216
# File 'lib/generators/react_on_rails/generator_helper.rb', line 214

def example_component_source_directory(component_name)
  File.join(shakapacker_source_path, "src", component_name)
end

#example_component_source_path(component_name) ⇒ Object



218
219
220
221
# File 'lib/generators/react_on_rails/generator_helper.rb', line 218

def example_component_source_path(component_name)
  # Trailing slash is intentional: this value is only for generated demo file hints.
  "#{example_component_source_directory(component_name)}/"
end

#explicit_bundler_choiceObject

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.



405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
# File 'lib/generators/react_on_rails/generator_helper.rb', line 405

def explicit_bundler_choice
  choices = []
  choices << options[:rspack] if options.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 = !options[:webpack]
  choices << rspack_via_webpack_flag if options.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

#extract_declared_layout_name(controller_content) ⇒ Object



191
192
193
194
# File 'lib/generators/react_on_rails/generator_helper.rb', line 191

def extract_declared_layout_name(controller_content)
  match = controller_content.match(CONTROLLER_LAYOUT_DECLARATION_PATTERN)
  match&.captures&.compact&.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: https://github.com/shakacode/react_on_rails/issues/2287

Parameters:

  • gem_name (String)

    Name of the gem to check

Returns:

  • (Boolean)

    true if the gem is in Gemfile.lock



326
327
328
329
330
331
# File 'lib/generators/react_on_rails/generator_helper.rb', line 326

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

#html_comment_ranges(content) ⇒ Object



179
180
181
182
183
184
185
# File 'lib/generators/react_on_rails/generator_helper.rb', line 179

def html_comment_ranges(content)
  content.to_enum(:scan, HTML_COMMENT_PATTERN).map do
    comment_match = Regexp.last_match

    comment_match.begin(0)...comment_match.end(0)
  end
end

#inherited_application_layout_nameObject



196
197
198
199
200
201
# File 'lib/generators/react_on_rails/generator_helper.rb', line 196

def inherited_application_layout_name
  application_controller_path = File.join(destination_root, "app/controllers/application_controller.rb")
  return "application" unless File.exist?(application_controller_path)

  extract_declared_layout_name(File.read(application_controller_path)) || "application"
end

#javascript_relative_import_path(from_file, to_file) ⇒ Object



282
283
284
285
286
287
# File 'lib/generators/react_on_rails/generator_helper.rb', line 282

def javascript_relative_import_path(from_file, to_file)
  from_dir = absolute_generator_path(from_file).dirname
  relative_path = absolute_generator_path(to_file).relative_path_from(from_dir).to_s

  relative_path.start_with?(".") ? relative_path : "./#{relative_path}"
end

#layout_destination_path(layout_name) ⇒ Object



203
204
205
# File 'lib/generators/react_on_rails/generator_helper.rb', line 203

def layout_destination_path(layout_name)
  "app/views/layouts/#{layout_name}.html.erb"
end

Returns:

  • (Boolean)


207
208
209
210
211
212
# File 'lib/generators/react_on_rails/generator_helper.rb', line 207

def layout_file_links_tailwind_pack?(layout_name)
  layout_full_path = File.join(destination_root, layout_destination_path(layout_name))
  return false unless File.exist?(layout_full_path)

  layout_links_tailwind_pack?(File.read(layout_full_path))
end

Returns:

  • (Boolean)


168
169
170
171
172
173
174
175
176
177
# File 'lib/generators/react_on_rails/generator_helper.rb', line 168

def layout_links_tailwind_pack?(content)
  comment_ranges = html_comment_ranges(content)
  return content.match?(TAILWIND_LAYOUT_PACK_HELPER_BLOCK_PATTERN) if comment_ranges.empty?

  content.to_enum(:scan, TAILWIND_LAYOUT_PACK_HELPER_BLOCK_PATTERN).any? do
    helper_match = Regexp.last_match

    !range_overlaps_any?(helper_match.begin(0)...helper_match.end(0), comment_ranges)
  end
end

#package_jsonObject



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/generators/react_on_rails/generator_helper.rb', line 36

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.message}", :yellow
  say_status :warning, "This is normal before Shakapacker creates the package.json file.", :yellow
  nil
end

#path_inside_or_equal?(child_path, parent_path) ⇒ Boolean

Returns:

  • (Boolean)


289
290
291
292
293
294
# File 'lib/generators/react_on_rails/generator_helper.rb', line 289

def path_inside_or_equal?(child_path, parent_path)
  relative_path = child_path.relative_path_from(parent_path).to_s
  relative_path == "." || (relative_path != ".." && !relative_path.start_with?("../"))
rescue ArgumentError
  false
end


91
92
93
94
95
96
97
98
# File 'lib/generators/react_on_rails/generator_helper.rb', line 91

def print_generator_messages
  # GeneratorMessages stores pre-colored strings, so we strip ANSI manually for --no-color output.
  no_color = !shell.is_a?(Thor::Shell::Color)
  GeneratorMessages.messages.each do |message|
    say(no_color ? message.to_s.gsub(/\e\[[0-9;]*m/, "") : message)
    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:

  1. Gem.loaded_specs - gem is loaded in current Ruby process (most reliable)
  2. 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.

Returns:

  • (Boolean)

    true if react_on_rails_pro gem is installed



344
345
346
347
348
# File 'lib/generators/react_on_rails/generator_helper.rb', line 344

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

#range_overlaps_any?(range, ranges) ⇒ Boolean

Returns:

  • (Boolean)


187
188
189
# File 'lib/generators/react_on_rails/generator_helper.rb', line 187

def range_overlaps_any?(range, ranges)
  ranges.any? { |candidate| range.begin < candidate.end && candidate.begin < range.end }
end

#relative_tailwind_stylesheet_import_pathObject



147
148
149
# File 'lib/generators/react_on_rails/generator_helper.rb', line 147

def relative_tailwind_stylesheet_import_path
  javascript_relative_import_path(tailwind_pack_path, tailwind_stylesheet_path)
end

#resolve_server_client_or_both_pathString?

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.

Returns:

  • (String, nil)

    relative config path, or nil if neither file exists



551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
# File 'lib/generators/react_on_rails/generator_helper.rb', line 551

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.

Parameters:

  • routes_path (String) (defaults to: File.join(destination_root, "config/routes.rb"))

    absolute path to routes.rb

Returns:

  • (Boolean)

    true when a root route exists



79
80
81
82
83
84
85
# File 'lib/generators/react_on_rails/generator_helper.rb', line 79

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

#rsc_plugin_class_nameString

RSC client-manifest plugin class name for the active bundler. Rspack uses the native RSCRspackPlugin; webpack uses RSCWebpackPlugin. Both expose the same { isServer, clientReferences } API and emit the same manifest schema, so only the import path and class name differ. Shared by the base webpack-config templates and the standalone RSC migration so both paths scaffold the bundler-correct plugin from one source of truth.

Returns:

  • (String)

    "RSCRspackPlugin" when rspack, "RSCWebpackPlugin" otherwise



457
458
459
# File 'lib/generators/react_on_rails/generator_helper.rb', line 457

def rsc_plugin_class_name
  using_rspack? ? "RSCRspackPlugin" : "RSCWebpackPlugin"
end

#rsc_plugin_import_pathString

react-on-rails-rsc subpath that exports #rsc_plugin_class_name.

Returns:

  • (String)

    "react-on-rails-rsc/RspackPlugin" when rspack, "react-on-rails-rsc/WebpackPlugin" otherwise



465
466
467
# File 'lib/generators/react_on_rails/generator_helper.rb', line 465

def rsc_plugin_import_path
  using_rspack? ? "react-on-rails-rsc/RspackPlugin" : "react-on-rails-rsc/WebpackPlugin"
end

#rspack_bundler_defaultObject

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.



433
434
435
# File 'lib/generators/react_on_rails/generator_helper.rb', line 433

def rspack_bundler_default
  rspack_configured_in_project?
end

#safe_generator_destination_path(path, default:, allow_root: false) ⇒ Object



246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/generators/react_on_rails/generator_helper.rb', line 246

def safe_generator_destination_path(path, default:, allow_root: false)
  candidate = path.to_s.strip
  return default if candidate.empty?

  pathname = Pathname.new(candidate).cleanpath
  # Shakapacker uses "/" to mean entrypoints live directly under source_path.
  return "" if allow_root && pathname.to_s == "/"

  relative_path = if pathname.absolute?
                    absolute_path_relative_to_destination(pathname)
                  else
                    pathname.to_s
                  end

  return default if unsafe_generator_destination_path?(relative_path)

  relative_path
rescue ArgumentError # Pathname.new raises on null bytes in path strings.
  default
end

#shakapacker_entrypoint_path(filename) ⇒ Object

Raises:

  • (ArgumentError)


118
119
120
121
122
123
124
# File 'lib/generators/react_on_rails/generator_helper.rb', line 118

def shakapacker_entrypoint_path(filename)
  filename = filename.to_s
  raise ArgumentError, "filename must be present" if filename.empty?

  entry_dir = shakapacker_source_entry_path # "" means entrypoints live directly under source_path.
  File.join(*[shakapacker_source_path, entry_dir, filename].reject(&:empty?))
end

#shakapacker_path_config_value(config, config_key) ⇒ Object



235
236
237
238
239
240
241
242
243
244
# File 'lib/generators/react_on_rails/generator_helper.rb', line 235

def shakapacker_path_config_value(config, config_key)
  # Generators run in the development context, so prefer that section before falling back to shared defaults.
  %w[development default].each do |section_name|
    section = shakapacker_config_section(config, section_name)
    value = shakapacker_config_value(section, config_key)
    return value unless value.to_s.strip.empty?
  end

  nil
end

#shakapacker_source_entry_pathObject



110
111
112
113
114
115
116
# File 'lib/generators/react_on_rails/generator_helper.rb', line 110

def shakapacker_source_entry_path
  @shakapacker_source_entry_path ||= configured_shakapacker_relative_path(
    "source_entry_path",
    DEFAULT_SHAKAPACKER_SOURCE_ENTRY_PATH,
    allow_root: true
  )
end

#shakapacker_source_pathObject



104
105
106
107
108
# File 'lib/generators/react_on_rails/generator_helper.rb', line 104

def shakapacker_source_path
  # These helpers memoize config-backed paths. Install generators must copy or
  # overwrite config/shakapacker.yml before any path-dependent copy action runs.
  @shakapacker_source_path ||= configured_shakapacker_relative_path("source_path", DEFAULT_SHAKAPACKER_SOURCE_PATH)
end

#shakapacker_stylesheet_path(filename) ⇒ Object



126
127
128
129
# File 'lib/generators/react_on_rails/generator_helper.rb', line 126

def shakapacker_stylesheet_path(filename)
  # "stylesheets" is a generated demo convention, not a Shakapacker config key.
  File.join(shakapacker_source_path, "stylesheets", filename)
end

#shakapacker_version_9_or_higher?Boolean

Note:

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).

Returns:

  • (Boolean)

    true if Shakapacker 9.0+ is available or likely to be installed



509
510
511
512
513
514
515
516
517
518
519
520
521
522
# File 'lib/generators/react_on_rails/generator_helper.rb', line 509

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

#stylesheet_dir_relative_path(stylesheet_dir, source_path) ⇒ Object



278
279
280
# File 'lib/generators/react_on_rails/generator_helper.rb', line 278

def stylesheet_dir_relative_path(stylesheet_dir, source_path)
  source_path.relative_path_from(stylesheet_dir).to_s
end

#tailwind_css_source_directivesObject



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/generators/react_on_rails/generator_helper.rb', line 151

def tailwind_css_source_directives
  stylesheet_dir = absolute_generator_path(tailwind_stylesheet_path).dirname
  rails_app_source = absolute_generator_path(RAILS_APP_SOURCE_PATH)
  shakapacker_source = absolute_generator_path(shakapacker_source_path)

  if path_inside_or_equal?(shakapacker_source, rails_app_source)
    rails_app_relative_source = stylesheet_dir_relative_path(stylesheet_dir, rails_app_source)
    tailwind_import_statement(source: rails_app_relative_source)
  else
    sources = [shakapacker_source, rails_app_source].uniq.map do |source_path|
      tailwind_source_statement(stylesheet_dir_relative_path(stylesheet_dir, source_path))
    end

    [tailwind_import_statement(source: "none"), *sources].join("\n")
  end
end

#tailwind_css_string(value) ⇒ Object



306
307
308
309
310
311
312
313
314
# File 'lib/generators/react_on_rails/generator_helper.rb', line 306

def tailwind_css_string(value)
  css_string = value.to_s
  if css_string.match?(/[[:cntrl:]\u2028\u2029]/)
    raise ArgumentError, "Tailwind source paths cannot contain control characters"
  end

  # JSON quoting keeps CSS punctuation inside the string literal.
  JSON.generate(css_string)
end

#tailwind_import_statement(source:) ⇒ Object



296
297
298
299
300
# File 'lib/generators/react_on_rails/generator_helper.rb', line 296

def tailwind_import_statement(source:)
  css_source = source == "none" ? "none" : tailwind_css_string(source)

  %( @import "tailwindcss" source(#{css_source});).strip
end

#tailwind_pack_filenameObject



135
136
137
# File 'lib/generators/react_on_rails/generator_helper.rb', line 135

def tailwind_pack_filename
  "#{tailwind_pack_name}.js"
end

#tailwind_pack_nameObject



131
132
133
# File 'lib/generators/react_on_rails/generator_helper.rb', line 131

def tailwind_pack_name
  TAILWIND_PACK_NAME
end

#tailwind_pack_pathObject



139
140
141
# File 'lib/generators/react_on_rails/generator_helper.rb', line 139

def tailwind_pack_path
  shakapacker_entrypoint_path(tailwind_pack_filename)
end

#tailwind_source_statement(source) ⇒ Object



302
303
304
# File 'lib/generators/react_on_rails/generator_helper.rb', line 302

def tailwind_source_statement(source)
  %( @source #{tailwind_css_string(source)};).strip
end

#tailwind_stylesheet_pathObject



143
144
145
# File 'lib/generators/react_on_rails/generator_helper.rb', line 143

def tailwind_stylesheet_path
  shakapacker_stylesheet_path(TAILWIND_STYLESHEET_NAME)
end

#unsafe_generator_destination_path?(path) ⇒ Boolean

Returns:

  • (Boolean)


316
317
318
# File 'lib/generators/react_on_rails/generator_helper.rb', line 316

def unsafe_generator_destination_path?(path)
  path.nil? || path == "." || path == ".." || path.start_with?("../")
end

#use_pro?Boolean

Check if Pro features should be enabled. Returns true if --pro or --rsc is set (RSC implies Pro).

Returns:

  • (Boolean)

    true if Pro setup should be included



354
355
356
# File 'lib/generators/react_on_rails/generator_helper.rb', line 354

def use_pro?
  options[:pro] || options[:rsc]
end

#use_rsc?Boolean

Check if RSC (React Server Components) should be enabled. Returns true if --rsc is set.

Returns:

  • (Boolean)

    true if RSC setup should be included



362
363
364
# File 'lib/generators/react_on_rails/generator_helper.rb', line 362

def use_rsc?
  options[:rsc]
end

#use_tailwind?Boolean

Check if Tailwind CSS should be installed and wired into the generated example.

Returns:

  • (Boolean)

    true if --tailwind is set



369
370
371
# File 'lib/generators/react_on_rails/generator_helper.rb', line 369

def use_tailwind?
  options[:tailwind]
end

#using_rspack?Boolean

Determine if the project is using rspack as the bundler.

Detection priority:

  1. Explicit --rspack option (most reliable during fresh installs)
  2. config/shakapacker.yml assets_bundler setting (for standalone generators like rails g react_on_rails:rsc on an existing rspack project)

Returns:

  • (Boolean)

    true if rspack is the configured bundler



381
382
383
384
385
386
387
388
389
# File 'lib/generators/react_on_rails/generator_helper.rb', line 381

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

Note:

This method is used to determine whether to install SWC dependencies (@swc/core, swc-loader) instead of Babel dependencies during generation.

Note:

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:

  1. If shakapacker.yml exists and specifies javascript_transpiler: parse it
  2. For Shakapacker 9.3.0+, SWC is the default if not specified
  3. Returns true for fresh installations (SWC is recommended default)

Returns:

  • (Boolean)

    true if SWC is configured or should be used by default



539
540
541
542
543
# File 'lib/generators/react_on_rails/generator_helper.rb', line 539

def using_swc?
  return @using_swc if defined?(@using_swc)

  @using_swc = detect_swc_configuration
end