lib/generators/svelte_on_rails/install/install_generator.rb



require "rails/generators"

module SvelteOnRails
  module Generators
    class InstallGenerator < Rails::Generators::Base

      class_option :full, type: :boolean, default: false, desc: "Run full installation with additional setup"
      class_option :force, type: :boolean, default: false, desc: "Do not ask for overwriting files"
      class_option :vite, type: :boolean, default: false, desc: "Use Vite"
      class_option :haml, type: :boolean, default: false, desc: "Use Haml"
      class_option :turbo, type: :boolean, default: false, desc: "Use @hotwired/turbo-rails"
      class_option :svelte, type: :boolean, default: false, desc: "Install Svelte"
      class_option :pug, type: :boolean, default: false, desc: "Install Pug"
      class_option :hello_world, type: :boolean, default: false, desc: "Create Hello World component"

      require 'svelte_on_rails/installer/utils'
      require 'svelte_on_rails/installer/haml'
      require 'svelte_on_rails/lib/development_utils'

      NPM_PACKAGE_NAME = '@csedl/svelte-on-rails'

      def initialize(*args)

        super

        @local_npm_package_url = SvelteOnRails::DevelopmentUtils.local_npm_package_url

        puts
        validate_raw_options!(args)
        puts '-' * 80
        puts '-' * 80
        puts 'STARTING SVELTE-ON-RAILS INSTALLATION'
        puts "  • FORCED (option --force was given)" if options[:force]
        if @local_npm_package_url
          puts "  • Local Npm Package used: #{NPM_PACKAGE_NAME} used: #{@local_npm_package_url}"
        end
        puts '-' * 80

      end

      def vite
        return unless options[:full] || options[:vite]
        puts '-' * 80
        puts '• INSTALLING VITE-RAILS'
        vite_i = SvelteOnRails::Installer::Vite
        vite_i.install_vite(major_version: 6)
      end

      def svelte_on_rails

        puts '-' * 80
        puts ' ▶︎▶︎▶︎ INSTALLING SVELTE-ON-RAILS GEM'

        utils = SvelteOnRails::Installer::Utils
        npm_i = SvelteOnRails::Installer::Npm
        utils.write_templates(['config_base'], ask_for_overwrite: !options[:force])

        # add script to package json
        pkg_js = JSON.parse(File.read('package.json'))
        pkg_js['scripts'] ||= { 'build:ssr': "vite build --config vite-ssr.config.ts" }
        File.write('package.json', JSON.pretty_generate(pkg_js))

        if @local_npm_package_url
          npm_i.link_local_package(NPM_PACKAGE_NAME, @local_npm_package_url)

          # npm does not resolve peer dependencies on local package url
          npm_i.install_or_update_package('@hotwired/stimulus')
        else
          npm_i.install_or_update_package(NPM_PACKAGE_NAME)
        end
        npm_i.install_or_update_package('typescript')
        npm_i.install_or_update_package('@types/node')

        js_i = SvelteOnRails::Installer::Javascript
        init_stat = '@csedl/svelte-on-rails'
        js_i.append_import_statement(application_js_path, init_stat, "import '#{init_stat}';")
      end

      def turbo
        return unless options[:full] || options[:turbo]

        puts '-' * 80
        puts ' ▶︎▶︎▶︎ INSTALLING @hotwired/turbo-rails'
        puts '-' * 80
        npm_i = SvelteOnRails::Installer::Npm
        tr_pkg = '@hotwired/turbo-rails'
        npm_i.install_or_update_package(tr_pkg)
        js_i = SvelteOnRails::Installer::Javascript
        js_i.append_import_statement(application_js_path, tr_pkg, "import '#{tr_pkg}';")

      end

      def svelte
        return unless options[:svelte] || options[:full]

        puts '-' * 80
        puts ' ▶︎▶︎▶︎ INSTALLING svelte (npm package)'

        puts '-' * 80
        svelte_i = SvelteOnRails::Installer::Svelte
        svelte_i.install_svelte
      end

      def hello_world
        return unless options[:hello_world] || options[:full]

        puts '-' * 80
        puts ' ▶︎▶︎▶︎︎ INSTALLING Hello World component'
        puts '-' * 80

        hw_i = SvelteOnRails::Installer::HelloWorld
        @hello_world_path = hw_i.install_hello_world(['rails_vite_hello_world'], app_root: nil, force: true, silent: true)
      end

      def haml_and_convert
        return unless options[:full] || options[:haml]

        puts '-' * 80
        puts ' ▶︎▶︎▶︎ INSTALLING Haml and converting existing .erb files to .haml'

        haml_i = SvelteOnRails::Installer::Haml
        haml_i.install_haml_and_convert(force: options[:force])
      end

      def finish
        puts '-' * 80
        puts ' 👍  FINISHED SVELTE-ON-RAILS INSTALLATION 👍'
        puts '-' * 80

        puts "SvelteOnRails installed successfully!"
        puts "Restart the server and check if it all works."
        if @hello_world_path
          puts "You can now access the Hello World component on: http://localhost:your-port-number#{@hello_world_path}."
        end
        puts "Happy coding!"
      end

      private

      def toggle_hello_world_files
        %w[
          app/frontend/images/svg.svg
          app/frontend/images/atom.svg
          app/frontend/javascript/nestedJavascript.js
          app/frontend/javascript/nestedJavascriptToggled.js
        ]
      end

      def validate_raw_options!(args)
        # Get option names from class_options, excluding inherited Thor/Rails options
        valid_options = self.class.class_options.keys.map { |opt| "--#{opt.to_s.tr('_', '-')}" }
        rails_internal_options = %w[--skip-namespace --skip-collision-check --pretend --quiet --force]
        options_array = args.find { |arg| arg.is_a?(Array) && arg.any? { |a| a.start_with?('--') } } || []
        passed_options = options_array.select { |arg| arg.start_with?('--') }
        unknown_options = passed_options - valid_options - rails_internal_options
        unless unknown_options.empty?
          valid_display = valid_options.map { |opt| opt.gsub(/^--/, '') }.join(', ')
          raise Thor::Error, "Unknown options: #{unknown_options.join(', ')}. Valid options are: #{valid_display}\nNothing done."
        end
      end

      def application_js_path
        Rails.root.join("app", "frontend", "entrypoints", "application.js")
      end
    end
  end
end