class Dependabot::NpmAndYarn::FileFetcher::PathDependencyBuilder

Attributes

dependency_name[R]
directory[R]
package_lock[R]
path[R]
yarn_lock[R]

Public Class Methods

new(dependency_name:, path:, directory:, package_lock:, yarn_lock:) click to toggle source
# File lib/dependabot/npm_and_yarn/file_fetcher/path_dependency_builder.rb, line 13
def initialize(dependency_name:, path:, directory:, package_lock:,
               yarn_lock:)
  @dependency_name = dependency_name
  @path = path
  @directory = directory
  @package_lock = package_lock
  @yarn_lock = yarn_lock
end

Public Instance Methods

dependency_file() click to toggle source
# File lib/dependabot/npm_and_yarn/file_fetcher/path_dependency_builder.rb, line 22
def dependency_file
  filename = File.join(path, "package.json")

  DependencyFile.new(
    name: Pathname.new(filename).cleanpath.to_path,
    content: build_path_dep_content(dependency_name),
    directory: directory,
    support_file: true
  )
end

Private Instance Methods

build_path_dep_content(dependency_name) click to toggle source
# File lib/dependabot/npm_and_yarn/file_fetcher/path_dependency_builder.rb, line 57
def build_path_dep_content(dependency_name)
  unless details_from_yarn_lock || details_from_npm_lock
    raise Dependabot::PathDependenciesNotReachable, [dependency_name]
  end

  if details_from_yarn_lock
    {
      name: dependency_name,
      version: details_from_yarn_lock["version"] || "0.0.1",
      dependencies:
        replace_yarn_lock_file_paths(
          details_from_yarn_lock["dependencies"]
        ),
      optionalDependencies:
        replace_yarn_lock_file_paths(
          details_from_yarn_lock["optionalDependencies"]
        )
    }.compact.to_json
  else
    {
      name: dependency_name,
      version: "0.0.1",
      dependencies: details_from_npm_lock["requires"]
    }.compact.to_json
  end
end
details_from_npm_lock() click to toggle source
# File lib/dependabot/npm_and_yarn/file_fetcher/path_dependency_builder.rb, line 48
def details_from_npm_lock
  path_starts = FileFetcher::NPM_PATH_DEPENDENCY_STARTS
  path_deps = parsed_package_lock.fetch("dependencies", []).to_a.
              select do |_, v|
                v.fetch("version", "").start_with?(*path_starts)
              end
  path_deps.find { |n, _| n == dependency_name }&.last
end
details_from_yarn_lock() click to toggle source
# File lib/dependabot/npm_and_yarn/file_fetcher/path_dependency_builder.rb, line 38
def details_from_yarn_lock
  path_starts = FileFetcher::PATH_DEPENDENCY_STARTS
  parsed_yarn_lock.to_a.
    find do |n, _|
      next false unless n.split(/(?<=\w)\@/).first == dependency_name

      n.split(/(?<=\w)\@/).last.start_with?(*path_starts)
    end&.last
end
inverted_path() click to toggle source

The path back to the root lockfile

# File lib/dependabot/npm_and_yarn/file_fetcher/path_dependency_builder.rb, line 129
def inverted_path
  path.split("/").map do |part|
    next part if part == "."
    next "tmp" if part == ".."

    ".."
  end.join("/")
end
parsed_package_lock() click to toggle source
# File lib/dependabot/npm_and_yarn/file_fetcher/path_dependency_builder.rb, line 113
def parsed_package_lock
  return {} unless package_lock

  JSON.parse(package_lock.content)
rescue JSON::ParserError
  {}
end
parsed_yarn_lock() click to toggle source
# File lib/dependabot/npm_and_yarn/file_fetcher/path_dependency_builder.rb, line 121
def parsed_yarn_lock
  return {} unless yarn_lock

  @parsed_yarn_lock ||=
    FileParser::YarnLockfileParser.new(lockfile: yarn_lock).parse
end
replace_yarn_lock_file_paths(dependencies_hash) click to toggle source

If an unfetchable path dependency itself has path dependencies then the paths in the yarn.lock for them will be absolute, not relative. Worse, they may point to the user's local cache. We work around this by constructing a relative path to the (second-level) path dependencies.

# File lib/dependabot/npm_and_yarn/file_fetcher/path_dependency_builder.rb, line 89
def replace_yarn_lock_file_paths(dependencies_hash)
  return unless dependencies_hash

  dependencies_hash.each_with_object({}) do |(name, value), obj|
    obj[name] = value
    next unless value.start_with?(*FileFetcher::PATH_DEPENDENCY_STARTS)

    path_from_base =
      parsed_yarn_lock.to_a.
      find do |n, _|
        next false unless n.split(/(?<=\w)\@/).first == name

        n.split(/(?<=\w)\@/).last.
          start_with?(*FileFetcher::PATH_DEPENDENCY_STARTS)
      end&.first&.split(/(?<=\w)\@/)&.last

    next unless path_from_base

    cleaned_path = path_from_base.
                   gsub(FileFetcher::PATH_DEPENDENCY_CLEAN_REGEX, "")
    obj[name] = "file:" + File.join(inverted_path, cleaned_path)
  end
end