lib/svelte_on_rails/installer/npm.rb



module SvelteOnRails
  module Installer
    module Npm

      def self.install_or_update_package(package_name, minimal_version: nil, update_to_latest: true)
        pkg = inspect_package(package_name)
        to_do = !check_version((pkg ? pkg[:version] : nil), minimal_version)
        if to_do

          cmd = if update_to_latest
                  "npm install #{package_name}@latest"
                else
                  raise "ERROR: not implemented"
                end

          stdout, stderr, status = Open3.capture3(cmd)
          if stderr.present?
            raise "ERROR #{cmd} => #{stderr}"
          end

          notice = [
            (pkg ? "Updated" : "Installed"),
            package_name,
            (pkg ? "from version #{pkg[:version].join('.')}" : nil),
            (pkg ? "to" : nil),
            "@latest"
          ].compact.join(' ')

          puts notice

        else
          min_str = minimal_version.present? ? ", required: >= #{minimal_version.join('.')}" : ''
          puts "#{package_name} already installed (#{pkg[:version].join('.')}#{min_str}), skipping."
        end
      end

      def self.inspect_package(package_name)
        pkg = nil
        version = nil

        if File.exist?('package-lock.json')
          pl = JSON.parse(File.read('package-lock.json'))
          unless pl['packages']
            raise "ERROR: package-lock.json found, but no packages found, seems to be corrupted."
          end
          pkg = pl['packages'].keys.grep(/\/#{package_name}$/).first
          version = pl['packages'][pkg]['version'] if pkg
        end

        if pkg
          {
            type: pkg.match(/^.*?(?=\/[^\/]*$)/).to_s,
            version: version.split('.').map(&:to_i)
          }
        end

      end

      def self.check_version(current_version, minimal_version)
        if !current_version
          return false
        elsif !minimal_version.present?
          return true
        else
          compare_version_arrays(current_version, minimal_version)
        end
      end

      private

      def self.compare_version_arrays(current_version, minimal_version)
        raise "ERROR: current_version must be an array" unless current_version.is_a?(Array)
        raise "ERROR: minimal_version must be an array" unless current_version.is_a?(Array)

        current_version.each_with_index do |v, i|
          raise "ERROR: current_version entries must be an integer" unless v.is_a?(Integer)

          if minimal_version[i]
            raise "ERROR: minimal_version entries must be an integer" unless minimal_version[i].is_a?(Integer)
            if v < minimal_version[i]
              return false
            end
          end
        end
        true
      end

    end
  end
end