lib/svelte_on_rails/lib/utils.rb



module SvelteOnRails
  module Lib
    class Utils

      def self.gem_app_dir
        File.expand_path('../../svelte_on_rails', __dir__) + '/'
      end

      def self.asset_path(filename)
        config = SvelteOnRails::Configuration.instance
        path = (config.frontend_folder + config.components_folder).join(filename)
        manifest = config.ssr_manifest
        manifest[path.to_s.sub(/\.svelte$/, '') + '.svelte']['file']
      end

      def self.file_exist_case_sensitive?(containing_dir, filename)
        # Combine the directory path and filename
        full_path = File.join(containing_dir, filename)

        # Check if the file exists and the path matches case-sensitively
        File.exist?(full_path) && Dir[File.join(containing_dir, "**/*")].any? do |f|
          f == full_path
        end
      end

      def self.component_files(filename, base_path: SvelteOnRails::Configuration.instance.components_folder_full)
        fn = (filename.match(/\.svelte$/) ? filename[0..-8] : filename)
        svelte_file = (base_path + fn).to_s + '.svelte'
        svelte_filename = fn + '.svelte'
        cnf = SvelteOnRails::Configuration.instance
        cf = cnf.rails_root.join('public', 'vite-ssr', asset_path(filename).sub(/.js$/, ''))

        {
          svelte_file: svelte_file,
          svelte_filename: svelte_filename,
          compiled_file: cf.to_s
        }
      end

      def self.watch_changes_and_precompile
        config = SvelteOnRails::Configuration.instance
        return unless config.watch_changes?

        mtime = Dir[File.join(config.components_folder_full, '**/*.svelte')].map do |file|
          File.mtime(file).to_f
        end.max || 0.0

        last = if Dir.exist?(config.ssr_dist_folder)
                 mtime_path = config.ssr_dist_folder.join('last_mtime')
                 (File.exist?(mtime_path) ? File.read(mtime_path).to_f : 0.0)
               end

        if !last || mtime > last
          precompile(mtime)
        end

      end

      def self.precompile(last_mtime = nil)
        config = SvelteOnRails::Configuration.instance

        mtime = (File.exist?(config.ssr_dist_folder.join('last_mtime')) ? File.read(config.ssr_dist_folder.join('last_mtime')).to_f : 0.0)

        Dir.chdir(config.rails_root) do

          # run build

          cmd = "./node_modules/.bin/vite build --config vite-ssr.config.ts"
          stdout, stderr, status = Open3.capture3(cmd)
          mtime2 = (File.exist?(config.ssr_dist_folder.join('last_mtime')) ? File.read(config.ssr_dist_folder.join('last_mtime')).to_f : 0.0)

          # error handling

          if stderr.present?

            nothing_done = mtime2 == mtime
            color = (nothing_done ? "\033[97;41m" : "\033[30;106m")

            msg = "  +++  #{nothing_done ? 'ERROR' : 'WARNING'} compiling Svelte components#{nothing_done ? ' failed' : ''}#{cmd}»)  +++  "
            puts "#{color}#{msg}\033[0m"
            errs = stderr.split("\n")
            errs.each { |e| puts "#{color}  \033[0m #{e}" }
            puts "#{color}  +++  End of error message  +++  \033[0m"
            puts "#{color}  +++  Run «npm run build:ssr» on the console to see the original error message  +++  \033[0m"

            if nothing_done
              critical_lines = errs.select { |e| e.match(/Could not resolve/i) }
              cl_str = if critical_lines.present?
                     "#{critical_lines.join("\n")}\n\n"
                   end
              raise "Svelte components compilation failed\n\n#{cl_str}Full message:\n+++\n#{stderr}+++\n\nYou can run «npm run build:ssr» on the console to see the original error message\n"
            end

          end

          puts stdout

        end

        unless Dir.exist?(config.ssr_dist_folder)
          raise "Could not find dist folder: #{config.ssr_dist_folder}"
        end
        if last_mtime
          mtime_path = config.ssr_dist_folder.join('last_mtime')
          File.write(mtime_path, last_mtime.to_s)
        end
      end

    end
  end
end