Class: ReactOnRails::Generators::BaseGenerator

Inherits:
Rails::Generators::Base
  • Object
show all
Includes:
GeneratorHelper, JsDependencyManager
Defined in:
lib/generators/react_on_rails/base_generator.rb

Constant Summary collapse

CONFIGURE_RSPEC_TO_COMPILE_ASSETS =
<<-STR.strip_heredoc
  RSpec.configure do |config|
    # Ensure that if we are running js tests, we are using latest webpack assets
    # This will use the defaults of :js and :server_rendering meta tags
    # Requires config.build_test_command in config/initializers/react_on_rails.rb.
    # This is the default setup for React on Rails generated apps.
    ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)
  end
STR
CONFIGURE_MINITEST_TO_COMPILE_ASSETS =
<<-STR.strip_heredoc
  # Ensure that tests run against fresh webpack assets.
  ActiveSupport::TestCase.setup do
    ReactOnRails::TestHelper.ensure_assets_compiled
  end
STR

Constants included from JsDependencyManager

JsDependencyManager::BABEL_REACT_DEPENDENCIES, JsDependencyManager::CSS_DEPENDENCIES, JsDependencyManager::DEV_DEPENDENCIES, JsDependencyManager::PRO_DEPENDENCIES, JsDependencyManager::REACT_DEPENDENCIES, JsDependencyManager::RSC_DEPENDENCIES, JsDependencyManager::RSPACK_DEPENDENCIES, JsDependencyManager::RSPACK_DEV_DEPENDENCIES, JsDependencyManager::SWC_DEPENDENCIES, JsDependencyManager::TYPESCRIPT_DEPENDENCIES

Instance Method Summary collapse

Methods included from GeneratorHelper

#add_documentation_reference, #add_npm_dependencies, #component_extension, #copy_file_and_missing_parent_directories, #dest_dir_exists?, #dest_file_exists?, #destination_config_path, #detect_react_version, #empty_directory_with_keep_file, #gem_in_lockfile?, #keep_file, #mark_pro_gem_installed!, #package_json, #print_generator_messages, #pro_gem_installed?, #resolve_server_client_or_both_path, #setup_file_error, #shakapacker_version_9_or_higher?, #symlink_dest_file_to_dest_file, #use_pro?, #use_rsc?, #using_rspack?, #using_swc?

Instance Method Details

#add_base_gems_to_gemfileObject



154
155
156
# File 'lib/generators/react_on_rails/base_generator.rb', line 154

def add_base_gems_to_gemfile
  run "bundle"
end

#add_hello_world_routeObject



50
51
52
53
54
55
# File 'lib/generators/react_on_rails/base_generator.rb', line 50

def add_hello_world_route
  # RSC uses HelloServer instead of HelloWorld, but Redux still needs hello_world route
  return if use_rsc? && !options.redux?

  route "get 'hello_world', to: 'hello_world#index'"
end

#append_to_spec_rails_helperObject



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

def append_to_spec_rails_helper
  rspec_helper = preferred_rspec_helper_file
  add_configure_rspec_to_compile_assets(rspec_helper) if rspec_helper

  test_helper = File.join(destination_root, "test/test_helper.rb")
  add_configure_minitest_to_compile_assets(test_helper) if File.exist?(test_helper)
end

#copy_base_filesObject



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/generators/react_on_rails/base_generator.rb', line 64

def copy_base_files
  base_path = "base/base/"
  base_files = %w[Procfile.dev
                  Procfile.dev-static-assets
                  Procfile.dev-prod-assets
                  .dev-services.yml.example
                  .env.example
                  bin/shakapacker-precompile-hook]

  # HelloServer uses the hello_world layout so React on Rails can inject generated
  # packs without requiring a hardcoded application.js pack entry.
  base_files << "app/views/layouts/hello_world.html.erb"

  # HelloWorld controller only when not using RSC (RSC uses HelloServer)
  # Exception: Redux still needs the HelloWorld controller even with RSC
  base_files << "app/controllers/hello_world_controller.rb" unless use_rsc? && !options.redux?
  base_templates = %w[config/initializers/react_on_rails.rb]
  base_files.each { |file| copy_file("#{base_path}#{file}", file) }
  base_templates.each do |file|
    template("#{base_path}/#{file}.tt", file)
  end

  # Make the hook script executable (copy_file guarantees it exists)
  if options[:pretend]
    say_status :pretend, "Skipping chmod on shakapacker-precompile-hook in --pretend mode", :yellow
  else
    File.chmod(0o755, File.join(destination_root, "bin/shakapacker-precompile-hook"))
  end
end

#copy_js_bundle_filesObject



94
95
96
97
98
99
100
101
102
103
104
# File 'lib/generators/react_on_rails/base_generator.rb', line 94

def copy_js_bundle_files
  base_path = "base/base/"
  base_files = %w[app/javascript/packs/server-bundle.js]

  # Skip HelloWorld CSS for Redux (uses HelloWorldApp) or RSC (uses HelloServer)
  unless options.redux? || use_rsc?
    base_files << "app/javascript/src/HelloWorld/ror_components/HelloWorld.module.css"
  end

  base_files.each { |file| copy_file("#{base_path}#{file}", file) }
end

#copy_packer_configObject



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/generators/react_on_rails/base_generator.rb', line 128

def copy_packer_config
  base_path = "base/base/"
  config = "config/shakapacker.yml"

  if options.shakapacker_just_installed?
    say "Replacing Shakapacker default config with React on Rails version"
    # Shakapacker's installer just created this file from scratch (no pre-existing config).
    # Safe to overwrite silently with RoR's version-aware template (e.g., private_output_path).
    template("#{base_path}#{config}.tt", config, force: true)
  else
    say "Adding Shakapacker #{ReactOnRails::PackerUtils.shakapacker_version} config"
    # Thor handles the conflict: prompts user interactively, or respects --force/--skip flags.
    template("#{base_path}#{config}.tt", config)
  end

  # Configure bundler-specific settings
  configure_rspack_in_shakapacker if using_rspack?

  # Always ensure precompile_hook is configured (Shakapacker 9.0+ only)
  configure_precompile_hook_in_shakapacker

  # For SSR bundles, configure Shakapacker private_output_path (9.0+ only)
  # This keeps Shakapacker and React on Rails server bundle paths in sync.
  configure_private_output_path_in_shakapacker
end

#copy_webpack_configObject



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/generators/react_on_rails/base_generator.rb', line 106

def copy_webpack_config
  say "Adding #{using_rspack? ? 'Rspack' : 'Webpack'} config"
  base_path = "base/base"
  base_files = %w[babel.config.js
                  config/webpack/clientWebpackConfig.js
                  config/webpack/commonWebpackConfig.js
                  config/webpack/test.js
                  config/webpack/development.js
                  config/webpack/production.js
                  config/webpack/serverWebpackConfig.js
                  config/webpack/ServerClientOrBoth.js]
  config = {
    message: "// The source code including full typescript support is available at:"
  }
  base_files.each do |file|
    template("#{base_path}/#{file}.tt", destination_config_path(file), config)
  end

  # Handle webpack.config.js separately with smart replacement
  copy_webpack_main_config(base_path, config)
end

#create_react_directoriesObject



57
58
59
60
61
62
# File 'lib/generators/react_on_rails/base_generator.rb', line 57

def create_react_directories
  # Skip HelloWorld directory for Redux (uses HelloWorldApp) or RSC (uses HelloServer)
  return if options.redux? || use_rsc?

  empty_directory("app/javascript/src/HelloWorld/ror_components")
end

#update_gitignore_for_auto_registrationObject



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/generators/react_on_rails/base_generator.rb', line 158

def update_gitignore_for_auto_registration
  gitignore_path = File.join(destination_root, ".gitignore")
  return unless File.exist?(gitignore_path)

  gitignore_content = File.read(gitignore_path)

  additions = []
  additions << "**/generated/**" unless gitignore_content.include?("**/generated/**")
  additions << "ssr-generated" unless gitignore_content.include?("ssr-generated")
  additions << ".env" unless gitignore_content.match?(/^\.env$/)

  return if additions.empty?

  append_to_file ".gitignore" do
    lines = ["\n# React on Rails (generated and local files)"]
    lines.concat(additions)
    "#{lines.join("\n")}\n"
  end
end