|  | 
| 4 | 4 | require "json" | 
| 5 | 5 | require_relative "generator_helper" | 
| 6 | 6 | require_relative "generator_messages" | 
|  | 7 | +require_relative "js_dependency_manager" | 
| 7 | 8 | 
 | 
| 8 | 9 | module ReactOnRails | 
| 9 | 10 |   module Generators | 
| 10 | 11 |     # rubocop:disable Metrics/ClassLength | 
| 11 | 12 |     class InstallGenerator < Rails::Generators::Base | 
| 12 | 13 |       include GeneratorHelper | 
|  | 14 | +      include JsDependencyManager | 
| 13 | 15 | 
 | 
| 14 | 16 |       # fetch USAGE file for details generator description | 
| 15 | 17 |       source_root(File.expand_path(__dir__)) | 
| @@ -83,10 +85,7 @@ def invoke_generators | 
| 83 | 85 |       end | 
| 84 | 86 | 
 | 
| 85 | 87 |       def setup_react_dependencies | 
| 86 |  | -        @added_dependencies_to_package_json ||= false | 
| 87 |  | -        @ran_direct_installs ||= false | 
| 88 |  | -        add_js_dependencies | 
| 89 |  | -        install_js_dependencies if @added_dependencies_to_package_json && !@ran_direct_installs | 
|  | 88 | +        setup_js_dependencies | 
| 90 | 89 |       end | 
| 91 | 90 | 
 | 
| 92 | 91 |       # NOTE: other requirements for existing files such as .gitignore or application. | 
| @@ -346,11 +345,17 @@ def install_typescript_dependencies | 
| 346 | 345 |         ] | 
| 347 | 346 | 
 | 
| 348 | 347 |         # Try using GeneratorHelper first (package manager agnostic) | 
| 349 |  | -        return if add_npm_dependencies(typescript_packages, dev: true) | 
|  | 348 | +        if add_npm_dependencies(typescript_packages, dev: true) | 
|  | 349 | +          @added_dependencies_to_package_json = true | 
|  | 350 | +          return | 
|  | 351 | +        end | 
| 350 | 352 | 
 | 
| 351 | 353 |         # Fallback to npm if GeneratorHelper fails | 
| 352 | 354 |         success = system("npm", "install", "--save-dev", *typescript_packages) | 
| 353 |  | -        return if success | 
|  | 355 | +        if success | 
|  | 356 | +          @ran_direct_installs = true | 
|  | 357 | +          return | 
|  | 358 | +        end | 
| 354 | 359 | 
 | 
| 355 | 360 |         warning = <<~MSG.strip | 
| 356 | 361 |           ⚠️  Failed to install TypeScript dependencies automatically. | 
| @@ -420,134 +425,6 @@ def create_typescript_config | 
| 420 | 425 |         puts Rainbow("✅ Created tsconfig.json").green | 
| 421 | 426 |       end | 
| 422 | 427 | 
 | 
| 423 |  | -      def add_js_dependencies | 
| 424 |  | -        add_react_on_rails_package | 
| 425 |  | -        add_react_dependencies | 
| 426 |  | -        add_css_dependencies | 
| 427 |  | -        add_dev_dependencies | 
| 428 |  | -      end | 
| 429 |  | - | 
| 430 |  | -      def add_react_on_rails_package | 
| 431 |  | -        major_minor_patch_only = /\A\d+\.\d+\.\d+\z/ | 
| 432 |  | - | 
| 433 |  | -        # Try to use package_json gem first, fall back to direct npm commands | 
| 434 |  | -        react_on_rails_pkg = if ReactOnRails::VERSION.match?(major_minor_patch_only) | 
| 435 |  | -                               ["react-on-rails@#{ReactOnRails::VERSION}"] | 
| 436 |  | -                             else | 
| 437 |  | -                               puts "Adding the latest react-on-rails NPM module. " \ | 
| 438 |  | -                                    "Double check this is correct in package.json" | 
| 439 |  | -                               ["react-on-rails"] | 
| 440 |  | -                             end | 
| 441 |  | - | 
| 442 |  | -        puts "Installing React on Rails package..." | 
| 443 |  | -        if add_npm_dependencies(react_on_rails_pkg) | 
| 444 |  | -          @added_dependencies_to_package_json = true | 
| 445 |  | -          return | 
| 446 |  | -        end | 
| 447 |  | - | 
| 448 |  | -        puts "Using direct npm commands as fallback" | 
| 449 |  | -        success = system("npm", "install", *react_on_rails_pkg) | 
| 450 |  | -        @ran_direct_installs = true if success | 
| 451 |  | -        handle_npm_failure("react-on-rails package", react_on_rails_pkg) unless success | 
| 452 |  | -      end | 
| 453 |  | - | 
| 454 |  | -      def add_react_dependencies | 
| 455 |  | -        puts "Installing React dependencies..." | 
| 456 |  | -        react_deps = %w[ | 
| 457 |  | -          react | 
| 458 |  | -          react-dom | 
| 459 |  | -          @babel/preset-react | 
| 460 |  | -          prop-types | 
| 461 |  | -          babel-plugin-transform-react-remove-prop-types | 
| 462 |  | -          babel-plugin-macros | 
| 463 |  | -        ] | 
| 464 |  | -        if add_npm_dependencies(react_deps) | 
| 465 |  | -          @added_dependencies_to_package_json = true | 
| 466 |  | -          return | 
| 467 |  | -        end | 
| 468 |  | - | 
| 469 |  | -        success = system("npm", "install", *react_deps) | 
| 470 |  | -        @ran_direct_installs = true if success | 
| 471 |  | -        handle_npm_failure("React dependencies", react_deps) unless success | 
| 472 |  | -      end | 
| 473 |  | - | 
| 474 |  | -      def add_css_dependencies | 
| 475 |  | -        puts "Installing CSS handling dependencies..." | 
| 476 |  | -        css_deps = %w[ | 
| 477 |  | -          css-loader | 
| 478 |  | -          css-minimizer-webpack-plugin | 
| 479 |  | -          mini-css-extract-plugin | 
| 480 |  | -          style-loader | 
| 481 |  | -        ] | 
| 482 |  | -        if add_npm_dependencies(css_deps) | 
| 483 |  | -          @added_dependencies_to_package_json = true | 
| 484 |  | -          return | 
| 485 |  | -        end | 
| 486 |  | - | 
| 487 |  | -        success = system("npm", "install", *css_deps) | 
| 488 |  | -        @ran_direct_installs = true if success | 
| 489 |  | -        handle_npm_failure("CSS dependencies", css_deps) unless success | 
| 490 |  | -      end | 
| 491 |  | - | 
| 492 |  | -      def add_dev_dependencies | 
| 493 |  | -        puts "Installing development dependencies..." | 
| 494 |  | -        dev_deps = %w[ | 
| 495 |  | -          @pmmmwh/react-refresh-webpack-plugin | 
| 496 |  | -          react-refresh | 
| 497 |  | -        ] | 
| 498 |  | -        if add_npm_dependencies(dev_deps, dev: true) | 
| 499 |  | -          @added_dependencies_to_package_json = true | 
| 500 |  | -          return | 
| 501 |  | -        end | 
| 502 |  | - | 
| 503 |  | -        success = system("npm", "install", "--save-dev", *dev_deps) | 
| 504 |  | -        @ran_direct_installs = true if success | 
| 505 |  | -        handle_npm_failure("development dependencies", dev_deps, dev: true) unless success | 
| 506 |  | -      end | 
| 507 |  | - | 
| 508 |  | -      def install_js_dependencies | 
| 509 |  | -        # Detect which package manager to use | 
| 510 |  | -        success = if File.exist?(File.join(destination_root, "yarn.lock")) | 
| 511 |  | -                    system("yarn", "install") | 
| 512 |  | -                  elsif File.exist?(File.join(destination_root, "pnpm-lock.yaml")) | 
| 513 |  | -                    system("pnpm", "install") | 
| 514 |  | -                  elsif File.exist?(File.join(destination_root, "package-lock.json")) || | 
| 515 |  | -                        File.exist?(File.join(destination_root, "package.json")) | 
| 516 |  | -                    # Use npm for package-lock.json or as default fallback | 
| 517 |  | -                    system("npm", "install") | 
| 518 |  | -                  else | 
| 519 |  | -                    true # No package manager detected, skip | 
| 520 |  | -                  end | 
| 521 |  | - | 
| 522 |  | -        unless success | 
| 523 |  | -          GeneratorMessages.add_warning(<<~MSG.strip) | 
| 524 |  | -            ⚠️  JavaScript dependencies installation failed. | 
| 525 |  | -
 | 
| 526 |  | -            This could be due to network issues or missing package manager. | 
| 527 |  | -            You can install dependencies manually later by running: | 
| 528 |  | -            • npm install (if using npm) | 
| 529 |  | -            • yarn install (if using yarn) | 
| 530 |  | -            • pnpm install (if using pnpm) | 
| 531 |  | -          MSG | 
| 532 |  | -        end | 
| 533 |  | - | 
| 534 |  | -        success | 
| 535 |  | -      end | 
| 536 |  | - | 
| 537 |  | -      def handle_npm_failure(dependency_type, packages, dev: false) | 
| 538 |  | -        install_command = dev ? "npm install --save-dev" : "npm install" | 
| 539 |  | -        GeneratorMessages.add_warning(<<~MSG.strip) | 
| 540 |  | -          ⚠️  Failed to install #{dependency_type}. | 
| 541 |  | -
 | 
| 542 |  | -          The following packages could not be installed automatically: | 
| 543 |  | -          #{packages.map { |pkg| "  • #{pkg}" }.join("\n")} | 
| 544 |  | -
 | 
| 545 |  | -          This could be due to network issues or missing package manager. | 
| 546 |  | -          You can install them manually later by running: | 
| 547 |  | -            #{install_command} #{packages.join(' ')} | 
| 548 |  | -        MSG | 
| 549 |  | -      end | 
| 550 |  | - | 
| 551 | 428 |       # Removed: Shakapacker auto-installation logic (now explicit dependency) | 
| 552 | 429 | 
 | 
| 553 | 430 |       # Removed: Shakapacker 8+ is now required as explicit dependency | 
|  | 
0 commit comments