reactjs/react-rails

View on GitHub
lib/generators/react/install_generator.rb

Summary

Maintainability
A
25 mins
Test Coverage
# frozen_string_literal: true

module React
  module Generators
    class InstallGenerator < ::Rails::Generators::Base
      source_root File.expand_path "../templates", __dir__

      desc "Create default react.js folder layout and prep application.js"

      class_option :skip_git,
                   type: :boolean,
                   aliases: "-g",
                   default: false,
                   desc: "Skip Git keeps"

      class_option :skip_server_rendering,
                   type: :boolean,
                   default: false,
                   desc: "Don't generate server_rendering.js or config/initializers/react_server_rendering.rb"

      # Make an empty `components/` directory in the right place:
      def create_directory
        components_dir = if shakapacker?
                           Pathname.new(javascript_dir).parent.to_s
                         else
                           javascript_dir
                         end
        empty_directory File.join(components_dir, "components")
        return if options[:skip_git]

        create_file File.join(components_dir, "components/.keep")
      end

      # Add requires, setup UJS
      def setup_react
        if shakapacker?
          setup_react_shakapacker
        else
          setup_react_sprockets
        end
      end

      def create_server_rendering
        if options[:skip_server_rendering]
          nil
        elsif shakapacker?
          ssr_manifest_path = File.join(javascript_dir, "server_rendering.js")
          template("server_rendering_pack.js", ssr_manifest_path)
        else
          ssr_manifest_path = File.join(javascript_dir, "server_rendering.js")
          template("server_rendering.js", ssr_manifest_path)
          initializer_path = "config/initializers/react_server_rendering.rb"
          template("react_server_rendering.rb", initializer_path)
        end
      end

      private

      def shakapacker?
        !!defined?(Shakapacker)
      end

      def javascript_dir
        if shakapacker?
          shakapacker_source_path
            .relative_path_from(::Rails.root)
            .to_s
        else
          "app/assets/javascripts"
        end
      end

      def manifest
        Pathname.new(destination_root).join(javascript_dir, "application.js")
      end

      def setup_react_sprockets
        require_react = "//= require react\n//= require react_ujs\n//= require components\n"

        if manifest.exist?
          manifest_contents = File.read(manifest)

          if (match = manifest_contents.match(%r{//=\s+require\s+turbolinks\s+\n}))
            inject_into_file manifest, require_react, { after: match[0] }
          elsif (match = manifest_contents.match(%r{//=\s+require_tree[^\n]*}))
            inject_into_file manifest, require_react, { before: match[0] }
          else
            append_file manifest, require_react
          end
        else
          create_file manifest, require_react
        end

        components_js = "//= require_tree ./components\n"
        components_file = File.join(javascript_dir, "components.js")
        create_file components_file, components_js
      end

      SHAKAPACKER_SETUP_UJS = <<~JS
        // Support component names relative to this directory:
        var componentRequireContext = require.context("components", true);
        var ReactRailsUJS = require("react_ujs");
        ReactRailsUJS.useContext(componentRequireContext);
      JS

      def require_package_json_gem
        require "bundler/inline"

        gemfile(true) { gem "package_json" }

        puts "using package_json v#{PackageJson::VERSION}"
      end

      def setup_react_shakapacker
        require_package_json_gem

        PackageJson.read.manager.add(["react_ujs"])

        if manifest.exist?
          append_file(manifest, SHAKAPACKER_SETUP_UJS)
        else
          create_file(manifest, SHAKAPACKER_SETUP_UJS)
        end
      end

      def shakapacker_source_path
        Shakapacker.config.source_entry_path
      end
    end
  end
end