dependabot-npm_and_yarn 0.291.0 → 0.293.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 02635cf238f21d329717cb8590e2c779109f30e53edb5a18d0af02c2eb1b7b52
4
- data.tar.gz: 05a8982b1c132c4560dbde94a72575a7ba62d9e9b1b3e6524d2cbcb2042f3eae
3
+ metadata.gz: 0d548c0891264c8b407f2b673e39266b3494683e431ac1e8b9ebc899319208f5
4
+ data.tar.gz: b43aeb89fb1a1c1e32a9d86eee5ccc188d8fc3da548ee4cbbcfbc4c76cfd59c7
5
5
  SHA512:
6
- metadata.gz: 69d8f7352749ea26e0aeee9ca63943fc6d46eccf927ec217fd9d5b072b60a405b5b7a4515c120e8e05145870ac1c0bc196c27ad38d4733c15e693af40d0055fa
7
- data.tar.gz: e5f8ad4e72213b0620785369b37c6cbf4d2200eea2a2ec521df6f6240694527216da0450af39cb86b7d9650d4d04649d5fc3bb4136163574ae29f2a3dc6db539
6
+ metadata.gz: 95eef6390619686a8017fc949e5a5609e34f3675920fea726975fb42c55904e9901ef55b80df005ffa43bf74e87623ed00a9a287005af9446c93b78fcb0c010d
7
+ data.tar.gz: 9a104350702acef102f005bdc0c4649b7713288de3685992538f9e58384487802f48a1f1c6e57dd6ac244fb11e849fd89b72f2f3648355eb3115d1174846e21f
@@ -0,0 +1,46 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ module Dependabot
5
+ module NpmAndYarn
6
+ class BunPackageManager < Ecosystem::VersionManager
7
+ extend T::Sig
8
+ NAME = "bun"
9
+ LOCKFILE_NAME = "bun.lock"
10
+
11
+ # In Bun 1.1.39, the lockfile format was changed from a binary bun.lockb to a text-based bun.lock.
12
+ # https://bun.sh/blog/bun-lock-text-lockfile
13
+ MIN_SUPPORTED_VERSION = Version.new("1.1.39")
14
+ SUPPORTED_VERSIONS = T.let([MIN_SUPPORTED_VERSION].freeze, T::Array[Dependabot::Version])
15
+ DEPRECATED_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version])
16
+
17
+ sig do
18
+ params(
19
+ detected_version: T.nilable(String),
20
+ raw_version: T.nilable(String),
21
+ requirement: T.nilable(Dependabot::NpmAndYarn::Requirement)
22
+ ).void
23
+ end
24
+ def initialize(detected_version: nil, raw_version: nil, requirement: nil)
25
+ super(
26
+ name: NAME,
27
+ detected_version: detected_version ? Version.new(detected_version) : nil,
28
+ version: raw_version ? Version.new(raw_version) : nil,
29
+ deprecated_versions: DEPRECATED_VERSIONS,
30
+ supported_versions: SUPPORTED_VERSIONS,
31
+ requirement: requirement
32
+ )
33
+ end
34
+
35
+ sig { override.returns(T::Boolean) }
36
+ def deprecated?
37
+ false
38
+ end
39
+
40
+ sig { override.returns(T::Boolean) }
41
+ def unsupported?
42
+ supported_versions.all? { |supported| supported > version }
43
+ end
44
+ end
45
+ end
46
+ end
@@ -82,7 +82,7 @@ module Dependabot
82
82
 
83
83
  sig { params(lockfile: DependencyFile).returns(T::Boolean) }
84
84
  def workspaces_lockfile?(lockfile)
85
- return false unless ["yarn.lock", "package-lock.json", "pnpm-lock.yaml"].include?(lockfile.name)
85
+ return false unless ["yarn.lock", "package-lock.json", "pnpm-lock.yaml", "bun.lock"].include?(lockfile.name)
86
86
 
87
87
  return false unless parsed_root_package_json["workspaces"] || dependency_files.any? do |file|
88
88
  file.name.end_with?("pnpm-workspace.yaml") && File.dirname(file.name) == File.dirname(lockfile.name)
@@ -148,6 +148,7 @@ module Dependabot
148
148
  "package-lock.json",
149
149
  "yarn.lock",
150
150
  "pnpm-lock.yaml",
151
+ "bun.lock",
151
152
  "npm-shrinkwrap.json"
152
153
  )
153
154
  end
@@ -68,6 +68,7 @@ module Dependabot
68
68
  package_managers["npm"] = npm_version if npm_version
69
69
  package_managers["yarn"] = yarn_version if yarn_version
70
70
  package_managers["pnpm"] = pnpm_version if pnpm_version
71
+ package_managers["bun"] = bun_version if bun_version
71
72
  package_managers["unknown"] = 1 if package_managers.empty?
72
73
 
73
74
  {
@@ -83,6 +84,7 @@ module Dependabot
83
84
  fetched_files += npm_files if npm_version
84
85
  fetched_files += yarn_files if yarn_version
85
86
  fetched_files += pnpm_files if pnpm_version
87
+ fetched_files += bun_files if bun_version
86
88
  fetched_files += lerna_files
87
89
  fetched_files += workspace_package_jsons
88
90
  fetched_files += path_dependencies(fetched_files)
@@ -120,6 +122,13 @@ module Dependabot
120
122
  fetched_pnpm_files
121
123
  end
122
124
 
125
+ sig { returns(T::Array[DependencyFile]) }
126
+ def bun_files
127
+ fetched_bun_files = []
128
+ fetched_bun_files << bun_lock if bun_lock
129
+ fetched_bun_files
130
+ end
131
+
123
132
  sig { returns(T::Array[DependencyFile]) }
124
133
  def lerna_files
125
134
  fetched_lerna_files = []
@@ -202,6 +211,16 @@ module Dependabot
202
211
  )
203
212
  end
204
213
 
214
+ sig { returns(T.nilable(T.any(Integer, String))) }
215
+ def bun_version
216
+ return @bun_version = nil unless Experiments.enabled?(:bun_updates)
217
+
218
+ @bun_version ||= T.let(
219
+ package_manager_helper.setup(BunPackageManager::NAME),
220
+ T.nilable(T.any(Integer, String))
221
+ )
222
+ end
223
+
205
224
  sig { returns(PackageManagerHelper) }
206
225
  def package_manager_helper
207
226
  @package_manager_helper ||= T.let(
@@ -219,7 +238,8 @@ module Dependabot
219
238
  {
220
239
  npm: package_lock || shrinkwrap,
221
240
  yarn: yarn_lock,
222
- pnpm: pnpm_lock
241
+ pnpm: pnpm_lock,
242
+ bun: bun_lock
223
243
  }
224
244
  end
225
245
 
@@ -261,17 +281,18 @@ module Dependabot
261
281
 
262
282
  return @pnpm_lock if @pnpm_lock || directory == "/"
263
283
 
264
- # Loop through parent directories looking for a pnpm-lock
265
- (1..directory.split("/").count).each do |i|
266
- @pnpm_lock = fetch_file_from_host(("../" * i) + PNPMPackageManager::LOCKFILE_NAME)
267
- .tap { |f| f.support_file = true }
268
- break if @pnpm_lock
269
- rescue Dependabot::DependencyFileNotFound
270
- # Ignore errors (pnpm_lock.yaml may not be present)
271
- nil
272
- end
284
+ @pnpm_lock = fetch_file_from_parent_directories(PNPMPackageManager::LOCKFILE_NAME)
285
+ end
286
+
287
+ sig { returns(T.nilable(DependencyFile)) }
288
+ def bun_lock
289
+ return @bun_lock if defined?(@bun_lock)
290
+
291
+ @bun_lock ||= T.let(fetch_file_if_present(BunPackageManager::LOCKFILE_NAME), T.nilable(DependencyFile))
273
292
 
274
- @pnpm_lock
293
+ return @bun_lock if @bun_lock || directory == "/"
294
+
295
+ @bun_lock = fetch_file_from_parent_directories(BunPackageManager::LOCKFILE_NAME)
275
296
  end
276
297
 
277
298
  sig { returns(T.nilable(DependencyFile)) }
@@ -294,17 +315,7 @@ module Dependabot
294
315
 
295
316
  return @npmrc if @npmrc || directory == "/"
296
317
 
297
- # Loop through parent directories looking for an npmrc
298
- (1..directory.split("/").count).each do |i|
299
- @npmrc = fetch_file_from_host(("../" * i) + NpmPackageManager::RC_FILENAME)
300
- .tap { |f| f.support_file = true }
301
- break if @npmrc
302
- rescue Dependabot::DependencyFileNotFound
303
- # Ignore errors (.npmrc may not be present)
304
- nil
305
- end
306
-
307
- @npmrc
318
+ @npmrc = fetch_file_from_parent_directories(NpmPackageManager::RC_FILENAME)
308
319
  end
309
320
 
310
321
  sig { returns(T.nilable(DependencyFile)) }
@@ -315,17 +326,7 @@ module Dependabot
315
326
 
316
327
  return @yarnrc if @yarnrc || directory == "/"
317
328
 
318
- # Loop through parent directories looking for an yarnrc
319
- (1..directory.split("/").count).each do |i|
320
- @yarnrc = fetch_file_from_host(("../" * i) + YarnPackageManager::RC_FILENAME)
321
- .tap { |f| f.support_file = true }
322
- break if @yarnrc
323
- rescue Dependabot::DependencyFileNotFound
324
- # Ignore errors (.yarnrc may not be present)
325
- nil
326
- end
327
-
328
- @yarnrc
329
+ @yarnrc = fetch_file_from_parent_directories(YarnPackageManager::RC_FILENAME)
329
330
  end
330
331
 
331
332
  sig { returns(T.nilable(DependencyFile)) }
@@ -699,6 +700,22 @@ module Dependabot
699
700
  Dependabot.logger.info("Repository contents path does not exist")
700
701
  end
701
702
  end
703
+
704
+ sig { params(filename: String).returns(T.nilable(DependencyFile)) }
705
+ def fetch_file_with_support(filename)
706
+ fetch_file_from_host(filename).tap { |f| f.support_file = true }
707
+ rescue Dependabot::DependencyFileNotFound
708
+ nil
709
+ end
710
+
711
+ sig { params(filename: String).returns(T.nilable(DependencyFile)) }
712
+ def fetch_file_from_parent_directories(filename)
713
+ (1..directory.split("/").count).each do |i|
714
+ file = fetch_file_with_support(("../" * i) + filename)
715
+ return file if file
716
+ end
717
+ nil
718
+ end
702
719
  end
703
720
  end
704
721
  end
@@ -0,0 +1,141 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "yaml"
5
+ require "dependabot/errors"
6
+ require "dependabot/npm_and_yarn/helpers"
7
+ require "sorbet-runtime"
8
+
9
+ module Dependabot
10
+ module NpmAndYarn
11
+ class FileParser < Dependabot::FileParsers::Base
12
+ class BunLock
13
+ extend T::Sig
14
+
15
+ sig { params(dependency_file: DependencyFile).void }
16
+ def initialize(dependency_file)
17
+ @dependency_file = dependency_file
18
+ end
19
+
20
+ sig { returns(T::Hash[String, T.untyped]) }
21
+ def parsed
22
+ @parsed ||= begin
23
+ content = begin
24
+ # Since bun.lock is a JSONC file, which is a subset of YAML, we can use YAML to parse it
25
+ YAML.load(T.must(@dependency_file.content))
26
+ rescue Psych::SyntaxError => e
27
+ raise_invalid!("malformed JSONC at line #{e.line}, column #{e.column}")
28
+ end
29
+ raise_invalid!("expected to be an object") unless content.is_a?(Hash)
30
+
31
+ version = content["lockfileVersion"]
32
+ raise_invalid!("expected 'lockfileVersion' to be an integer") unless version.is_a?(Integer)
33
+ raise_invalid!("expected 'lockfileVersion' to be >= 0") unless version >= 0
34
+ raise_invalid!("unsupported 'lockfileVersion' = #{version}") unless version.zero?
35
+
36
+ T.let(content, T.untyped)
37
+ end
38
+ end
39
+
40
+ sig { returns(Dependabot::FileParsers::Base::DependencySet) }
41
+ def dependencies
42
+ dependency_set = Dependabot::FileParsers::Base::DependencySet.new
43
+
44
+ # bun.lock v0 format:
45
+ # https://github.com/oven-sh/bun/blob/c130df6c589fdf28f9f3c7f23ed9901140bc9349/src/install/bun.lock.zig#L595-L605
46
+
47
+ packages = parsed["packages"]
48
+ raise_invalid!("expected 'packages' to be an object") unless packages.is_a?(Hash)
49
+
50
+ packages.each do |key, details|
51
+ raise_invalid!("expected 'packages.#{key}' to be an array") unless details.is_a?(Array)
52
+
53
+ resolution = details.first
54
+ raise_invalid!("expected 'packages.#{key}[0]' to be a string") unless resolution.is_a?(String)
55
+
56
+ name, version = resolution.split(/(?<=\w)\@/)
57
+ next if name.empty?
58
+
59
+ semver = Version.semver_for(version)
60
+ next unless semver
61
+
62
+ dependency_set << Dependency.new(
63
+ name: name,
64
+ version: semver.to_s,
65
+ package_manager: "npm_and_yarn",
66
+ requirements: []
67
+ )
68
+ end
69
+
70
+ dependency_set
71
+ end
72
+
73
+ sig do
74
+ params(dependency_name: String, requirement: T.untyped, _manifest_name: String)
75
+ .returns(T.nilable(T::Hash[String, T.untyped]))
76
+ end
77
+ def details(dependency_name, requirement, _manifest_name)
78
+ packages = parsed["packages"]
79
+ return unless packages.is_a?(Hash)
80
+
81
+ candidates =
82
+ packages
83
+ .select { |name, _| name == dependency_name }
84
+ .values
85
+
86
+ # If there's only one entry for this dependency, use it, even if
87
+ # the requirement in the lockfile doesn't match
88
+ if candidates.one?
89
+ parse_details(candidates.first)
90
+ else
91
+ candidate = candidates.find do |label, _|
92
+ label.scan(/(?<=\w)\@(?:npm:)?([^\s,]+)/).flatten.include?(requirement)
93
+ end&.last
94
+ parse_details(candidate)
95
+ end
96
+ end
97
+
98
+ private
99
+
100
+ sig { params(message: String).void }
101
+ def raise_invalid!(message)
102
+ raise Dependabot::DependencyFileNotParseable.new(@dependency_file.path, "Invalid bun.lock file: #{message}")
103
+ end
104
+
105
+ sig do
106
+ params(entry: T.nilable(T::Array[T.untyped])).returns(T.nilable(T::Hash[String, T.untyped]))
107
+ end
108
+ def parse_details(entry)
109
+ return unless entry.is_a?(Array)
110
+
111
+ # Either:
112
+ # - "{name}@{version}", registry, details, integrity
113
+ # - "{name}@{resolution}", details
114
+ resolution = entry.first
115
+ return unless resolution.is_a?(String)
116
+
117
+ name, version = resolution.split(/(?<=\w)\@/)
118
+ semver = Version.semver_for(version)
119
+
120
+ if semver
121
+ registry, details, integrity = entry[1..3]
122
+ {
123
+ "name" => name,
124
+ "version" => semver.to_s,
125
+ "registry" => registry,
126
+ "details" => details,
127
+ "integrity" => integrity
128
+ }
129
+ else
130
+ details = entry[1]
131
+ {
132
+ "name" => name,
133
+ "resolution" => version,
134
+ "details" => details
135
+ }
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
@@ -15,6 +15,11 @@ module Dependabot
15
15
  require "dependabot/npm_and_yarn/file_parser/yarn_lock"
16
16
  require "dependabot/npm_and_yarn/file_parser/pnpm_lock"
17
17
  require "dependabot/npm_and_yarn/file_parser/json_lock"
18
+ require "dependabot/npm_and_yarn/file_parser/bun_lock"
19
+
20
+ DEFAULT_LOCKFILES = %w(package-lock.json yarn.lock pnpm-lock.yaml bun.lock npm-shrinkwrap.json).freeze
21
+
22
+ LockFile = T.type_alias { T.any(JsonLock, YarnLock, PnpmLock, BunLock) }
18
23
 
19
24
  sig { params(dependency_files: T::Array[DependencyFile]).void }
20
25
  def initialize(dependency_files:)
@@ -29,7 +34,7 @@ module Dependabot
29
34
  # end up unique by name. That's not a perfect representation of
30
35
  # the nested nature of JS resolution, but it makes everything work
31
36
  # comparably to other flat-resolution strategies
32
- (yarn_locks + pnpm_locks + package_locks + shrinkwraps).each do |file|
37
+ (yarn_locks + pnpm_locks + package_locks + bun_locks + shrinkwraps).each do |file|
33
38
  dependency_set += lockfile_for(file).dependencies
34
39
  end
35
40
 
@@ -64,58 +69,59 @@ module Dependabot
64
69
  sig { params(manifest_filename: String).returns(T::Array[DependencyFile]) }
65
70
  def potential_lockfiles_for_manifest(manifest_filename)
66
71
  dir_name = File.dirname(manifest_filename)
67
- possible_lockfile_names =
68
- %w(package-lock.json npm-shrinkwrap.json pnpm-lock.yaml yarn.lock).map do |f|
69
- Pathname.new(File.join(dir_name, f)).cleanpath.to_path
70
- end +
71
- %w(yarn.lock pnpm-lock.yaml package-lock.json npm-shrinkwrap.json)
72
+ possible_lockfile_names = DEFAULT_LOCKFILES.map do |f|
73
+ Pathname.new(File.join(dir_name, f)).cleanpath.to_path
74
+ end + DEFAULT_LOCKFILES
72
75
 
73
76
  possible_lockfile_names.uniq
74
77
  .filter_map { |nm| dependency_files.find { |f| f.name == nm } }
75
78
  end
76
79
 
77
- sig { params(file: DependencyFile).returns(T.any(JsonLock, YarnLock, PnpmLock)) }
80
+ sig { params(file: DependencyFile).returns(LockFile) }
78
81
  def lockfile_for(file)
79
- @lockfiles ||= T.let({}, T.nilable(T::Hash[String, T.any(JsonLock, YarnLock, PnpmLock)]))
80
- @lockfiles[file.name] ||= if [*package_locks, *shrinkwraps].include?(file)
82
+ @lockfiles ||= T.let({}, T.nilable(T::Hash[String, LockFile]))
83
+ @lockfiles[file.name] ||= case file.name
84
+ when *package_locks.map(&:name), *shrinkwraps.map(&:name)
81
85
  JsonLock.new(file)
82
- elsif yarn_locks.include?(file)
86
+ when *yarn_locks.map(&:name)
83
87
  YarnLock.new(file)
84
- else
88
+ when *pnpm_locks.map(&:name)
85
89
  PnpmLock.new(file)
90
+ when *bun_locks.map(&:name)
91
+ BunLock.new(file)
92
+ else
93
+ raise "Unexpected lockfile: #{file.name}"
86
94
  end
87
95
  end
88
96
 
97
+ sig { params(extension: String).returns(T::Array[DependencyFile]) }
98
+ def select_files_by_extension(extension)
99
+ dependency_files.select { |f| f.name.end_with?(extension) }
100
+ end
101
+
89
102
  sig { returns(T::Array[DependencyFile]) }
90
103
  def package_locks
91
- @package_locks ||= T.let(
92
- dependency_files
93
- .select { |f| f.name.end_with?("package-lock.json") }, T.nilable(T::Array[DependencyFile])
94
- )
104
+ @package_locks ||= T.let(select_files_by_extension("package-lock.json"), T.nilable(T::Array[DependencyFile]))
95
105
  end
96
106
 
97
107
  sig { returns(T::Array[DependencyFile]) }
98
108
  def pnpm_locks
99
- @pnpm_locks ||= T.let(
100
- dependency_files
101
- .select { |f| f.name.end_with?("pnpm-lock.yaml") }, T.nilable(T::Array[DependencyFile])
102
- )
109
+ @pnpm_locks ||= T.let(select_files_by_extension("pnpm-lock.yaml"), T.nilable(T::Array[DependencyFile]))
110
+ end
111
+
112
+ sig { returns(T::Array[DependencyFile]) }
113
+ def bun_locks
114
+ @bun_locks ||= T.let(select_files_by_extension("bun.lock"), T.nilable(T::Array[DependencyFile]))
103
115
  end
104
116
 
105
117
  sig { returns(T::Array[DependencyFile]) }
106
118
  def yarn_locks
107
- @yarn_locks ||= T.let(
108
- dependency_files
109
- .select { |f| f.name.end_with?("yarn.lock") }, T.nilable(T::Array[DependencyFile])
110
- )
119
+ @yarn_locks ||= T.let(select_files_by_extension("yarn.lock"), T.nilable(T::Array[DependencyFile]))
111
120
  end
112
121
 
113
122
  sig { returns(T::Array[DependencyFile]) }
114
123
  def shrinkwraps
115
- @shrinkwraps ||= T.let(
116
- dependency_files
117
- .select { |f| f.name.end_with?("npm-shrinkwrap.json") }, T.nilable(T::Array[DependencyFile])
118
- )
124
+ @shrinkwraps ||= T.let(select_files_by_extension("npm-shrinkwrap.json"), T.nilable(T::Array[DependencyFile]))
119
125
  end
120
126
 
121
127
  sig { returns(T.class_of(Dependabot::NpmAndYarn::Version)) }
@@ -26,6 +26,10 @@ module Dependabot
26
26
  end
27
27
 
28
28
  def dependencies
29
+ if Dependabot::Experiments.enabled?(:enable_fix_for_pnpm_no_change_error)
30
+ return dependencies_with_prioritization
31
+ end
32
+
29
33
  dependency_set = Dependabot::FileParsers::Base::DependencySet.new
30
34
 
31
35
  parsed.each do |details|
@@ -52,6 +56,49 @@ module Dependabot
52
56
  dependency_set
53
57
  end
54
58
 
59
+ def dependencies_with_prioritization
60
+ dependency_set = Dependabot::FileParsers::Base::DependencySet.new
61
+
62
+ # Separate dependencies into two categories: with specifiers and without specifiers.
63
+ dependencies_with_specifiers = [] # Main dependencies with specifiers.
64
+ dependencies_without_specifiers = [] # Subdependencies without specifiers.
65
+
66
+ parsed.each do |details|
67
+ next if details["aliased"]
68
+
69
+ name = details["name"]
70
+ version = details["version"]
71
+
72
+ dependency_args = {
73
+ name: name,
74
+ version: version,
75
+ package_manager: "npm_and_yarn",
76
+ requirements: []
77
+ }
78
+
79
+ # Add metadata for subdependencies if marked as a dev dependency.
80
+ dependency_args[:subdependency_metadata] = [{ production: !details["dev"] }] if details["dev"]
81
+
82
+ specifiers = details["specifiers"]
83
+ if specifiers&.any?
84
+ dependencies_with_specifiers << dependency_args
85
+ else
86
+ dependencies_without_specifiers << dependency_args
87
+ end
88
+ end
89
+
90
+ # Add prioritized dependencies to the dependency set.
91
+ dependencies_with_specifiers.each do |dependency_args|
92
+ dependency_set << Dependency.new(**dependency_args)
93
+ end
94
+
95
+ dependencies_without_specifiers.each do |dependency_args|
96
+ dependency_set << Dependency.new(**dependency_args)
97
+ end
98
+
99
+ dependency_set
100
+ end
101
+
55
102
  def details(dependency_name, requirement, _manifest_name)
56
103
  details_candidates = parsed.select { |info| info["name"] == dependency_name }
57
104
 
@@ -110,7 +110,8 @@ module Dependabot
110
110
  {
111
111
  npm: package_lock || shrinkwrap,
112
112
  yarn: yarn_lock,
113
- pnpm: pnpm_lock
113
+ pnpm: pnpm_lock,
114
+ bun: bun_lock
114
115
  }
115
116
  end
116
117
 
@@ -167,6 +168,13 @@ module Dependabot
167
168
  end, T.nilable(Dependabot::DependencyFile))
168
169
  end
169
170
 
171
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
172
+ def bun_lock
173
+ @bun_lock ||= T.let(dependency_files.find do |f|
174
+ f.name == BunPackageManager::LOCKFILE_NAME
175
+ end, T.nilable(Dependabot::DependencyFile))
176
+ end
177
+
170
178
  sig { returns(T.nilable(Dependabot::DependencyFile)) }
171
179
  def npmrc
172
180
  @npmrc ||= T.let(dependency_files.find do |f|