class ReactOnRails::Generators::ReactWithReduxGenerator
def add_redux_npm_dependencies
def add_redux_npm_dependencies # Add Redux dependencies as regular dependencies regular_packages = %w[redux react-redux] # Try using GeneratorHelper first (package manager agnostic) success = add_npm_dependencies(regular_packages) # Fallback to package manager detection if GeneratorHelper fails return if success package_manager = GeneratorMessages.detect_package_manager return unless package_manager install_packages_with_fallback(regular_packages, dev: false, package_manager: package_manager) end
def add_redux_specific_messages
def add_redux_specific_messages # Append Redux-specific post-install instructions GeneratorMessages.add_info( GeneratorMessages.helpful_message_after_installation(component_name: "HelloWorldApp", route: "hello_world") ) end
def build_install_args(package_manager, dev, packages)
def build_install_args(package_manager, dev, packages) # Security: Validate package manager to prevent command injection allowed_package_managers = %w[npm yarn pnpm bun].freeze unless allowed_package_managers.include?(package_manager) raise ArgumentError, "Invalid package manager: #{package_manager}" end base_commands = { "npm" => %w[npm install], "yarn" => %w[yarn add], "pnpm" => %w[pnpm add], "bun" => %w[bun add] } base_args = base_commands[package_manager].dup base_args << dev_flag_for(package_manager) if dev base_args + packages end
def copy_base_files
def copy_base_files base_js_path = "redux/base" ext = component_extension(options) # Copy Redux-connected component to auto-bundling structure copy_file("#{base_js_path}/app/javascript/bundles/HelloWorld/startup/HelloWorldApp.client.#{ext}", "app/javascript/src/HelloWorldApp/ror_components/HelloWorldApp.client.#{ext}") copy_file("#{base_js_path}/app/javascript/bundles/HelloWorld/startup/HelloWorldApp.server.#{ext}", "app/javascript/src/HelloWorldApp/ror_components/HelloWorldApp.server.#{ext}") copy_file("#{base_js_path}/app/javascript/bundles/HelloWorld/components/HelloWorld.module.css", "app/javascript/src/HelloWorldApp/components/HelloWorld.module.css") # Update import paths in client component ror_client_file = "app/javascript/src/HelloWorldApp/ror_components/HelloWorldApp.client.#{ext}" gsub_file(ror_client_file, "../store/helloWorldStore", "../store/helloWorldStore") gsub_file(ror_client_file, "../containers/HelloWorldContainer", "../containers/HelloWorldContainer") end
def copy_base_redux_files
def copy_base_redux_files base_hello_world_path = "redux/base/app/javascript/bundles/HelloWorld" redux_extension = options.typescript? ? "ts" : "js" # Copy Redux infrastructure files with appropriate extension %W[actions/helloWorldActionCreators.#{redux_extension} containers/HelloWorldContainer.#{redux_extension} constants/helloWorldConstants.#{redux_extension} reducers/helloWorldReducer.#{redux_extension} store/helloWorldStore.#{redux_extension} components/HelloWorld.#{component_extension(options)}].each do |file| copy_file("#{base_hello_world_path}/#{file}", "app/javascript/src/HelloWorldApp/#{file}") end end
def create_appropriate_templates
def create_appropriate_templates base_path = "base/base" config = { component_name: "HelloWorldApp" } # Only create the view template - no manual bundle needed for auto-bundling template("#{base_path}/app/views/hello_world/index.html.erb.tt", "app/views/hello_world/index.html.erb", config) end
def create_redux_directories
def create_redux_directories # Create auto-bundling directory structure for Redux empty_directory("app/javascript/src/HelloWorldApp/ror_components") # Create Redux support directories within the component directory dirs = %w[actions constants containers reducers store components] dirs.each { |name| empty_directory("app/javascript/src/HelloWorldApp/#{name}") } end
def dev_flag_for(package_manager)
def dev_flag_for(package_manager) case package_manager when "npm", "pnpm" then "--save-dev" when "yarn", "bun" then "--dev" end end
def install_packages_with_fallback(packages, dev:, package_manager:)
def install_packages_with_fallback(packages, dev:, package_manager:) install_args = build_install_args(package_manager, dev, packages) success = system(*install_args) return if success install_command = install_args.join(" ") warning = <<~MSG.strip ⚠️ Failed to install Redux dependencies automatically. Please run manually: #{install_command} MSG GeneratorMessages.add_warning(warning) end