class Licensed::Sources::NPM

Public Instance Methods

enabled?() click to toggle source
# File lib/licensed/sources/npm.rb, line 26
def enabled?
  Licensed::Shell.tool_available?("npm") && File.exist?(package_json_path)
end
enumerate_dependencies() click to toggle source
# File lib/licensed/sources/npm.rb, line 30
def enumerate_dependencies
  packages.map do |name, package|
    next if package["name"] == project_name

    errors = package["problems"] unless package["path"]
    Dependency.new(
      name: name,
      version: package["version"] || package["required"],
      path: package["path"],
      errors: Array(errors),
      metadata: {
        "type"     => NPM.type,
        "name"     => package["name"],
        "summary"  => package["description"],
        "homepage" => package["homepage"]
      }
    )
  end
end
extract_version(parent, name) click to toggle source
# File lib/licensed/sources/npm.rb, line 161
def extract_version(parent, name)
  parent&.dig("_dependencies", name) || peer_dependency(parent, name)
end
include_non_production?() click to toggle source

Returns whether to include non production dependencies based on the licensed configuration settings

# File lib/licensed/sources/npm.rb, line 135
def include_non_production?
  config.dig("npm", "production_only") == false
end
missing_peer?(parent, dependency, name) click to toggle source
# File lib/licensed/sources/npm.rb, line 139
def missing_peer?(parent, dependency, name)
  # return true if dependency is marked as "peerMissing"
  return true if dependency["peerMissing"]

  # return false unless the parent has registered the dependency
  # as a peer
  return false unless peer_dependency(parent, name)
  # return true if the dependency itself is marked as missing
  return true if dependency["missing"]
  dependency.empty? && parent&.dig("peerDependenciesMeta", name, "optional")
end
npm_version() click to toggle source

Returns the currently installed version of npm as a Gem::Version object

# File lib/licensed/sources/npm.rb, line 123
def npm_version
  @npm_version ||= begin
    Gem::Version.new(Licensed::Shell.execute("npm", "-v").strip)
  end
end
package_json() click to toggle source

Returns the parse package.json for the current project

# File lib/licensed/sources/npm.rb, line 172
def package_json
  return unless File.exist?(package_json_path)

  @package_json ||= JSON.parse(File.read(package_json_path))
rescue JSON::ParserError => e
  message = "Licensed was unable to parse package.json. JSON Error: #{e.message}"
  raise Licensed::Sources::Source::Error, message
end
package_json_path() click to toggle source
# File lib/licensed/sources/npm.rb, line 181
def package_json_path
  @package_json_path ||= File.join(config.pwd, "package.json")
end
package_metadata() click to toggle source

Returns parsed package metadata returned from ‘npm list`

# File lib/licensed/sources/npm.rb, line 83
def package_metadata
  return @package_metadata if defined?(@package_metadata)
  @package_metadata = JSON.parse(package_metadata_command)
rescue JSON::ParserError => e
  message = "Licensed was unable to parse the output from 'npm list'. JSON Error: #{e.message}"
  npm_error = package_metadata_error
  message = "#{message}. npm Error: #{npm_error}" if npm_error
  raise Licensed::Sources::Source::Error, message
end
package_metadata_args() click to toggle source

Returns an array of arguments that should be used for all ‘npm list` calls, regardless of how the output is formatted

# File lib/licensed/sources/npm.rb, line 111
def package_metadata_args
  args = []
  args << "--production" unless include_non_production?

  # on npm 7+, the --all argument is necessary to evaluate the project's
  # full dependency tree
  args << "--all" if npm_version >= Gem::Version.new("7.0.0")

  return args
end
package_metadata_command() click to toggle source

Returns the output from running ‘npm list` to get package metadata

# File lib/licensed/sources/npm.rb, line 102
def package_metadata_command
  args = %w(--json --long)
  args.concat(package_metadata_args)

  Licensed::Shell.execute("npm", "list", *args, allow_failure: true)
end
package_metadata_error() click to toggle source

Returns an error, if one exists, from running ‘npm list` to get package metadata

# File lib/licensed/sources/npm.rb, line 94
def package_metadata_error
  Licensed::Shell.execute("npm", "list", *package_metadata_args)
  return ""
rescue Licensed::Shell::Error => e
  return e.message
end
packages() click to toggle source
# File lib/licensed/sources/npm.rb, line 50
def packages
  root_dependencies = package_metadata["dependencies"]
  recursive_dependencies(root_dependencies).each_with_object({}) do |(name, results), hsh|
    results.uniq! { |package| package["version"] }
    if results.size == 1
      hsh[name] = results[0]
    else
      results.each do |package|
        name_with_version = "#{name}-#{package["version"]}"
        hsh[name_with_version] = package
      end
    end
  end
end
peer_dependency(parent, name) click to toggle source
# File lib/licensed/sources/npm.rb, line 151
def peer_dependency(parent, name)
  return unless parent.is_a?(Hash)

  peerDependencies = parent["peerDependencies"]
  # "peerDependencies" could be set to the string "[Circular]"
  return unless peerDependencies.is_a?(Hash)

  peerDependencies[name]
end
project_name() click to toggle source

Returns the current projects name

# File lib/licensed/sources/npm.rb, line 166
def project_name
  return unless package_json
  package_json["name"]
end
recursive_dependencies(dependencies, result = {}, parent = nil) click to toggle source

Recursively parse dependency JSON data. Returns a hash mapping the package name to it’s metadata

# File lib/licensed/sources/npm.rb, line 67
def recursive_dependencies(dependencies, result = {}, parent = nil)
  dependencies.each do |name, dependency|
    next if missing_peer?(parent, dependency, name)
    next if yarn_lock_present && dependency["missing"]
    next if dependency["extraneous"] && dependency["missing"]

    dependency["name"] = name
    dependency["version"] ||= extract_version(parent, name) if dependency["missing"]

    (result[name] ||= []) << dependency
    recursive_dependencies(dependency["dependencies"] || {}, result, dependency)
  end
  result
end
yarn_lock_present() click to toggle source

Returns true if a yarn.lock file exists in the current directory

# File lib/licensed/sources/npm.rb, line 130
def yarn_lock_present
  @yarn_lock_present ||= File.exist?(config.pwd.join("yarn.lock"))
end