pivotal/LicenseFinder

View on GitHub
lib/license_finder/package_managers/pnpm.rb

Summary

Maintainability
A
3 hrs
Test Coverage
# frozen_string_literal: true

require 'json'
require 'tempfile'

module LicenseFinder
  class PNPM < PackageManager
    def initialize(options = {})
      super
      @pnpm_options = options[:pnpm_options]
    end

    SHELL_COMMAND = 'pnpm licenses list --json --long'

    def possible_package_paths
      [project_path.join('pnpm-lock.yaml')]
    end

    def self.takes_priority_over
      NPM
    end

    def current_packages
      # check if the minimum version of PNPM is met
      raise 'The minimum PNPM version is not met, requires 7.17.0 or later' unless supported_pnpm?

      # check if the project directory has workspace file
      cmd = PNPM::SHELL_COMMAND.to_s
      cmd += ' --no-color'
      cmd += ' --recursive' unless project_has_workspaces == false
      cmd += " --dir #{project_path}" unless project_path.nil?
      cmd += " #{@pnpm_options}" unless @pnpm_options.nil?

      stdout, stderr, status = Cmd.run(cmd)
      raise "Command '#{cmd}' failed to execute: #{stderr}" unless status.success?

      json_objects = JSON.parse(stdout)
      get_pnpm_packages(json_objects)
    end

    def get_pnpm_packages(json_objects)
      packages = []
      incompatible_packages = []

      json_objects.map do |_, value|
        value.each do |pkg|
          name = pkg['name']

          if pkg['version']
            version = pkg['version']
          elsif pkg['versions']
            version = pkg['versions'][0]
          end

          license = pkg['license']
          homepage = pkg['vendorUrl']
          author = pkg['vendorName']
          module_path = pkg['path']

          package = PNPMPackage.new(
            name,
            version,
            spec_licenses: [license],
            homepage: homepage,
            authors: author,
            install_path: module_path
          )
          packages << package
        end
      end

      packages + incompatible_packages.uniq
    end

    def package_management_command
      'pnpm'
    end

    def prepare_command
      'pnpm install --no-lockfile --ignore-scripts'
    end

    def prepare
      prep_cmd = "#{prepare_command}#{production_flag}"
      _stdout, stderr, status = Dir.chdir(project_path) { Cmd.run(prep_cmd) }

      return if status.success?

      log_errors stderr
      raise "Prepare command '#{prep_cmd}' failed" unless @prepare_no_fail
    end

    private

    def project_has_workspaces
      Dir.chdir(project_path) do
        return File.file?('pnpm-workspace.yaml')
      end
    end

    # PNPM introduced the licenses command in 7.17.0
    def supported_pnpm?
      Dir.chdir(project_path) do
        version_string, stderr_str, status = Cmd.run('pnpm --version')
        raise "Command 'pnpm -v' failed to execute: #{stderr_str}" unless status.success?

        version = version_string.split('.').map(&:to_i)
        major = version[0]
        minor = version[1]
        patch = version[1]

        return true if major > 7
        return true if major == 7 && minor > 17
        return true if major == 7 && minor == 17 && patch >= 0

        return false
      end
    end

    def production_flag
      return '' if @ignored_groups.nil?

      @ignored_groups.include?('devDependencies') ? ' --prod' : ''
    end
  end
end