dependabot-bun 0.296.2 → 0.296.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/.eslintrc +11 -0
  3. data/helpers/README.md +29 -0
  4. data/helpers/build +26 -0
  5. data/helpers/jest.config.js +5 -0
  6. data/helpers/lib/npm/conflicting-dependency-parser.js +78 -0
  7. data/helpers/lib/npm/index.js +9 -0
  8. data/helpers/lib/npm/vulnerability-auditor.js +291 -0
  9. data/helpers/lib/npm6/helpers.js +25 -0
  10. data/helpers/lib/npm6/index.js +9 -0
  11. data/helpers/lib/npm6/peer-dependency-checker.js +111 -0
  12. data/helpers/lib/npm6/remove-dependencies-from-lockfile.js +22 -0
  13. data/helpers/lib/npm6/subdependency-updater.js +78 -0
  14. data/helpers/lib/npm6/updater.js +199 -0
  15. data/helpers/lib/pnpm/index.js +5 -0
  16. data/helpers/lib/pnpm/lockfile-parser.js +82 -0
  17. data/helpers/lib/yarn/conflicting-dependency-parser.js +176 -0
  18. data/helpers/lib/yarn/fix-duplicates.js +80 -0
  19. data/helpers/lib/yarn/helpers.js +54 -0
  20. data/helpers/lib/yarn/index.js +14 -0
  21. data/helpers/lib/yarn/lockfile-parser.js +21 -0
  22. data/helpers/lib/yarn/peer-dependency-checker.js +132 -0
  23. data/helpers/lib/yarn/replace-lockfile-declaration.js +57 -0
  24. data/helpers/lib/yarn/subdependency-updater.js +83 -0
  25. data/helpers/lib/yarn/updater.js +209 -0
  26. data/helpers/package-lock.json +28519 -0
  27. data/helpers/package.json +29 -0
  28. data/helpers/patches/npm++pacote+9.5.12.patch +14 -0
  29. data/helpers/run.js +30 -0
  30. data/helpers/test/npm6/conflicting-dependency-parser.test.js +66 -0
  31. data/helpers/test/npm6/fixtures/conflicting-dependency-parser/deeply-nested/package-lock.json +591 -0
  32. data/helpers/test/npm6/fixtures/conflicting-dependency-parser/deeply-nested/package.json +14 -0
  33. data/helpers/test/npm6/fixtures/conflicting-dependency-parser/nested/package-lock.json +188 -0
  34. data/helpers/test/npm6/fixtures/conflicting-dependency-parser/nested/package.json +14 -0
  35. data/helpers/test/npm6/fixtures/conflicting-dependency-parser/simple/package-lock.json +27 -0
  36. data/helpers/test/npm6/fixtures/conflicting-dependency-parser/simple/package.json +14 -0
  37. data/helpers/test/npm6/fixtures/updater/original/package-lock.json +16 -0
  38. data/helpers/test/npm6/fixtures/updater/original/package.json +9 -0
  39. data/helpers/test/npm6/fixtures/updater/updated/package-lock.json +16 -0
  40. data/helpers/test/npm6/helpers.js +21 -0
  41. data/helpers/test/npm6/updater.test.js +30 -0
  42. data/helpers/test/pnpm/fixtures/parser/empty_version/pnpm-lock.yaml +72 -0
  43. data/helpers/test/pnpm/fixtures/parser/no_lockfile_change/pnpm-lock.yaml +2744 -0
  44. data/helpers/test/pnpm/fixtures/parser/only_dev_dependencies/pnpm-lock.yaml +16 -0
  45. data/helpers/test/pnpm/fixtures/parser/peer_disambiguation/pnpm-lock.yaml +855 -0
  46. data/helpers/test/pnpm/lockfile-parser.test.js +62 -0
  47. data/helpers/test/yarn/conflicting-dependency-parser.test.js +83 -0
  48. data/helpers/test/yarn/fixtures/conflicting-dependency-parser/deeply-nested/package.json +14 -0
  49. data/helpers/test/yarn/fixtures/conflicting-dependency-parser/deeply-nested/yarn.lock +496 -0
  50. data/helpers/test/yarn/fixtures/conflicting-dependency-parser/dev-dependencies/package.json +14 -0
  51. data/helpers/test/yarn/fixtures/conflicting-dependency-parser/dev-dependencies/yarn.lock +21 -0
  52. data/helpers/test/yarn/fixtures/conflicting-dependency-parser/nested/package.json +14 -0
  53. data/helpers/test/yarn/fixtures/conflicting-dependency-parser/nested/yarn.lock +183 -0
  54. data/helpers/test/yarn/fixtures/conflicting-dependency-parser/simple/package.json +14 -0
  55. data/helpers/test/yarn/fixtures/conflicting-dependency-parser/simple/yarn.lock +21 -0
  56. data/helpers/test/yarn/fixtures/updater/illegal_character/package.json +8 -0
  57. data/helpers/test/yarn/fixtures/updater/illegal_character/yarn.lock +14 -0
  58. data/helpers/test/yarn/fixtures/updater/original/package.json +6 -0
  59. data/helpers/test/yarn/fixtures/updater/original/yarn.lock +11 -0
  60. data/helpers/test/yarn/fixtures/updater/updated/yarn.lock +12 -0
  61. data/helpers/test/yarn/fixtures/updater/with-version-comments/package.json +5 -0
  62. data/helpers/test/yarn/fixtures/updater/with-version-comments/yarn.lock +13 -0
  63. data/helpers/test/yarn/helpers.js +18 -0
  64. data/helpers/test/yarn/updater.test.js +117 -0
  65. data/lib/dependabot/bun/bun_package_manager.rb +47 -0
  66. data/lib/dependabot/bun/constraint_helper.rb +359 -0
  67. data/lib/dependabot/bun/dependency_files_filterer.rb +157 -0
  68. data/lib/dependabot/bun/file_fetcher/path_dependency_builder.rb +184 -0
  69. data/lib/dependabot/bun/file_fetcher.rb +402 -0
  70. data/lib/dependabot/bun/file_parser/bun_lock.rb +140 -0
  71. data/lib/dependabot/bun/file_parser/lockfile_parser.rb +105 -0
  72. data/lib/dependabot/bun/file_parser.rb +477 -0
  73. data/lib/dependabot/bun/file_updater/bun_lockfile_updater.rb +144 -0
  74. data/lib/dependabot/bun/file_updater/npmrc_builder.rb +256 -0
  75. data/lib/dependabot/bun/file_updater/package_json_preparer.rb +88 -0
  76. data/lib/dependabot/bun/file_updater/package_json_updater.rb +378 -0
  77. data/lib/dependabot/bun/file_updater.rb +203 -0
  78. data/lib/dependabot/bun/helpers.rb +93 -0
  79. data/lib/dependabot/bun/language.rb +45 -0
  80. data/lib/dependabot/bun/metadata_finder.rb +214 -0
  81. data/lib/dependabot/bun/native_helpers.rb +19 -0
  82. data/lib/dependabot/bun/package_manager.rb +280 -0
  83. data/lib/dependabot/bun/package_name.rb +118 -0
  84. data/lib/dependabot/bun/pnpm_package_manager.rb +55 -0
  85. data/lib/dependabot/bun/registry_helper.rb +188 -0
  86. data/lib/dependabot/bun/registry_parser.rb +93 -0
  87. data/lib/dependabot/bun/requirement.rb +146 -0
  88. data/lib/dependabot/bun/sub_dependency_files_filterer.rb +82 -0
  89. data/lib/dependabot/bun/update_checker/conflicting_dependency_resolver.rb +59 -0
  90. data/lib/dependabot/bun/update_checker/dependency_files_builder.rb +79 -0
  91. data/lib/dependabot/bun/update_checker/latest_version_finder.rb +448 -0
  92. data/lib/dependabot/bun/update_checker/library_detector.rb +76 -0
  93. data/lib/dependabot/bun/update_checker/registry_finder.rb +279 -0
  94. data/lib/dependabot/bun/update_checker/requirements_updater.rb +206 -0
  95. data/lib/dependabot/bun/update_checker/subdependency_version_resolver.rb +154 -0
  96. data/lib/dependabot/bun/update_checker/version_resolver.rb +583 -0
  97. data/lib/dependabot/bun/update_checker/vulnerability_auditor.rb +164 -0
  98. data/lib/dependabot/bun/update_checker.rb +455 -0
  99. data/lib/dependabot/bun/version.rb +138 -0
  100. data/lib/dependabot/bun/version_selector.rb +61 -0
  101. data/lib/dependabot/bun.rb +337 -35
  102. metadata +108 -65
  103. data/lib/dependabot/javascript/bun/file_fetcher.rb +0 -77
  104. data/lib/dependabot/javascript/bun/file_parser/bun_lock.rb +0 -156
  105. data/lib/dependabot/javascript/bun/file_parser/lockfile_parser.rb +0 -55
  106. data/lib/dependabot/javascript/bun/file_parser.rb +0 -74
  107. data/lib/dependabot/javascript/bun/file_updater/lockfile_updater.rb +0 -138
  108. data/lib/dependabot/javascript/bun/file_updater.rb +0 -75
  109. data/lib/dependabot/javascript/bun/helpers.rb +0 -72
  110. data/lib/dependabot/javascript/bun/package_manager.rb +0 -48
  111. data/lib/dependabot/javascript/bun/requirement.rb +0 -11
  112. data/lib/dependabot/javascript/bun/update_checker/conflicting_dependency_resolver.rb +0 -64
  113. data/lib/dependabot/javascript/bun/update_checker/dependency_files_builder.rb +0 -47
  114. data/lib/dependabot/javascript/bun/update_checker/latest_version_finder.rb +0 -450
  115. data/lib/dependabot/javascript/bun/update_checker/library_detector.rb +0 -76
  116. data/lib/dependabot/javascript/bun/update_checker/requirements_updater.rb +0 -203
  117. data/lib/dependabot/javascript/bun/update_checker/subdependency_version_resolver.rb +0 -144
  118. data/lib/dependabot/javascript/bun/update_checker/version_resolver.rb +0 -525
  119. data/lib/dependabot/javascript/bun/update_checker/vulnerability_auditor.rb +0 -165
  120. data/lib/dependabot/javascript/bun/update_checker.rb +0 -440
  121. data/lib/dependabot/javascript/bun/version.rb +0 -11
  122. data/lib/dependabot/javascript/shared/constraint_helper.rb +0 -359
  123. data/lib/dependabot/javascript/shared/dependency_files_filterer.rb +0 -164
  124. data/lib/dependabot/javascript/shared/file_fetcher.rb +0 -283
  125. data/lib/dependabot/javascript/shared/file_parser/lockfile_parser.rb +0 -106
  126. data/lib/dependabot/javascript/shared/file_parser.rb +0 -454
  127. data/lib/dependabot/javascript/shared/file_updater/npmrc_builder.rb +0 -394
  128. data/lib/dependabot/javascript/shared/file_updater/package_json_preparer.rb +0 -87
  129. data/lib/dependabot/javascript/shared/file_updater/package_json_updater.rb +0 -376
  130. data/lib/dependabot/javascript/shared/file_updater.rb +0 -179
  131. data/lib/dependabot/javascript/shared/language.rb +0 -45
  132. data/lib/dependabot/javascript/shared/metadata_finder.rb +0 -209
  133. data/lib/dependabot/javascript/shared/native_helpers.rb +0 -21
  134. data/lib/dependabot/javascript/shared/package_manager_detector.rb +0 -72
  135. data/lib/dependabot/javascript/shared/package_name.rb +0 -118
  136. data/lib/dependabot/javascript/shared/registry_helper.rb +0 -190
  137. data/lib/dependabot/javascript/shared/registry_parser.rb +0 -93
  138. data/lib/dependabot/javascript/shared/requirement.rb +0 -144
  139. data/lib/dependabot/javascript/shared/sub_dependency_files_filterer.rb +0 -79
  140. data/lib/dependabot/javascript/shared/update_checker/dependency_files_builder.rb +0 -87
  141. data/lib/dependabot/javascript/shared/update_checker/registry_finder.rb +0 -358
  142. data/lib/dependabot/javascript/shared/version.rb +0 -133
  143. data/lib/dependabot/javascript/shared/version_selector.rb +0 -60
  144. data/lib/dependabot/javascript.rb +0 -39
@@ -0,0 +1,583 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ require "dependabot/errors"
7
+ require "dependabot/git_commit_checker"
8
+ require "dependabot/logger"
9
+ require "dependabot/bun/dependency_files_filterer"
10
+ require "dependabot/bun/file_parser"
11
+ require "dependabot/bun/file_updater/npmrc_builder"
12
+ require "dependabot/bun/file_updater/package_json_preparer"
13
+ require "dependabot/bun/helpers"
14
+ require "dependabot/bun/native_helpers"
15
+ require "dependabot/bun/package_name"
16
+ require "dependabot/bun/requirement"
17
+ require "dependabot/bun/update_checker"
18
+ require "dependabot/bun/version"
19
+ require "dependabot/shared_helpers"
20
+
21
+ # rubocop:disable Metrics/ClassLength
22
+ module Dependabot
23
+ module Bun
24
+ class UpdateChecker
25
+ class VersionResolver
26
+ extend T::Sig
27
+
28
+ require_relative "latest_version_finder"
29
+
30
+ TIGHTLY_COUPLED_MONOREPOS = {
31
+ "vue" => %w(vue vue-template-compiler)
32
+ }.freeze
33
+
34
+ # Error message returned by `npm install` (for NPM 6):
35
+ # react-dom@15.2.0 requires a peer of react@^15.2.0 \
36
+ # but none is installed. You must install peer dependencies yourself.
37
+ NPM6_PEER_DEP_ERROR_REGEX =
38
+ /
39
+ (?<requiring_dep>[^\s]+)\s
40
+ requires\sa\speer\sof\s
41
+ (?<required_dep>.+?)\sbut\snone\sis\sinstalled.
42
+ /x
43
+
44
+ # Error message returned by `npm install` (for NPM 8):
45
+ # npm ERR! Could not resolve dependency:
46
+ # npm ERR! peer react@"^16.14.0" from react-dom@16.14.0
47
+ #
48
+ # or with two semver constraints:
49
+ # npm ERR! Could not resolve dependency:
50
+ # npm ERR! peer @opentelemetry/api@">=1.0.0 <1.1.0" from @opentelemetry/context-async-hooks@1.0.1
51
+ NPM8_PEER_DEP_ERROR_REGEX =
52
+ /
53
+ npm\s(?:WARN|ERR!)\sCould\snot\sresolve\sdependency:\n
54
+ npm\s(?:WARN|ERR!)\speer\s(?<required_dep>\S+@\S+(\s\S+)?)\sfrom\s(?<requiring_dep>\S+@\S+)
55
+ /x
56
+
57
+ def initialize(dependency:, credentials:, dependency_files:,
58
+ latest_allowable_version:, latest_version_finder:, repo_contents_path:, dependency_group: nil)
59
+ @dependency = dependency
60
+ @credentials = credentials
61
+ @dependency_files = dependency_files
62
+ @latest_allowable_version = latest_allowable_version
63
+ @dependency_group = dependency_group
64
+
65
+ @latest_version_finder = {}
66
+ @latest_version_finder[dependency] = latest_version_finder
67
+ @repo_contents_path = repo_contents_path
68
+ end
69
+
70
+ def latest_resolvable_version
71
+ return latest_allowable_version if git_dependency?(dependency)
72
+ return if part_of_tightly_locked_monorepo?
73
+ return if types_update_available?
74
+ return if original_package_update_available?
75
+
76
+ return latest_allowable_version unless relevant_unmet_peer_dependencies.any?
77
+
78
+ satisfying_versions.first
79
+ end
80
+
81
+ def latest_version_resolvable_with_full_unlock?
82
+ return false if dependency_updates_from_full_unlock.nil?
83
+
84
+ true
85
+ end
86
+
87
+ def latest_resolvable_previous_version(updated_version)
88
+ resolve_latest_previous_version(dependency, updated_version)
89
+ end
90
+
91
+ # rubocop:disable Metrics/PerceivedComplexity
92
+ def dependency_updates_from_full_unlock
93
+ return if git_dependency?(dependency)
94
+ return updated_monorepo_dependencies if part_of_tightly_locked_monorepo?
95
+ return if newly_broken_peer_reqs_from_dep.any?
96
+ return if original_package_update_available?
97
+
98
+ updates = [{
99
+ dependency: dependency,
100
+ version: latest_allowable_version,
101
+ previous_version: latest_resolvable_previous_version(
102
+ latest_allowable_version
103
+ )
104
+ }]
105
+ newly_broken_peer_reqs_on_dep.each do |peer_req|
106
+ dep_name = peer_req.fetch(:requiring_dep_name)
107
+ dep = top_level_dependencies.find { |d| d.name == dep_name }
108
+
109
+ # Can't handle reqs from sub-deps or git source deps (yet)
110
+ return nil if dep.nil?
111
+ return nil if git_dependency?(dep)
112
+
113
+ updated_version =
114
+ latest_version_of_dep_with_satisfied_peer_reqs(dep)
115
+ return nil unless updated_version
116
+
117
+ updates << {
118
+ dependency: dep,
119
+ version: updated_version,
120
+ previous_version: resolve_latest_previous_version(
121
+ dep, updated_version
122
+ )
123
+ }
124
+ end
125
+ updates += updated_types_dependencies if types_update_available?
126
+ updates.uniq
127
+ end
128
+ # rubocop:enable Metrics/PerceivedComplexity
129
+
130
+ private
131
+
132
+ sig { returns(Dependabot::Dependency) }
133
+ attr_reader :dependency
134
+ attr_reader :credentials
135
+ attr_reader :dependency_files
136
+ attr_reader :latest_allowable_version
137
+ attr_reader :repo_contents_path
138
+ attr_reader :dependency_group
139
+
140
+ def latest_version_finder(dep)
141
+ @latest_version_finder[dep] ||=
142
+ LatestVersionFinder.new(
143
+ dependency: dep,
144
+ credentials: credentials,
145
+ dependency_files: dependency_files,
146
+ ignored_versions: [],
147
+ security_advisories: []
148
+ )
149
+ end
150
+
151
+ # rubocop:disable Metrics/PerceivedComplexity
152
+ def resolve_latest_previous_version(dep, updated_version)
153
+ return dep.version if dep.version
154
+
155
+ @resolve_latest_previous_version ||= {}
156
+ @resolve_latest_previous_version[dep] ||= begin
157
+ relevant_versions = latest_version_finder(dependency)
158
+ .possible_previous_versions_with_details
159
+ .map(&:first)
160
+ reqs = dep.requirements.filter_map { |r| r[:requirement] }
161
+ .map { |r| requirement_class.requirements_array(r) }
162
+
163
+ # Pick the lowest version from the max possible version from all
164
+ # requirements. This matches the logic when combining the same
165
+ # dependency in DependencySet from multiple manifest files where we
166
+ # pick the lowest version from the duplicates.
167
+ latest_previous_version = reqs.flat_map do |req|
168
+ relevant_versions.select do |version|
169
+ req.any? { |r| r.satisfied_by?(version) }
170
+ end.max
171
+ end.min&.to_s
172
+
173
+ # Handle cases where the latest resolvable previous version is the
174
+ # latest version. This often happens if you don't have lockfiles and
175
+ # have requirements update strategy set to bump_versions, where an
176
+ # update might go from ^1.1.1 to ^1.1.2 (both resolve to 1.1.2).
177
+ if updated_version.to_s == latest_previous_version
178
+ nil
179
+ else
180
+ latest_previous_version
181
+ end
182
+ end
183
+ end
184
+ # rubocop:enable Metrics/PerceivedComplexity
185
+
186
+ def part_of_tightly_locked_monorepo?
187
+ monorepo_dep_names =
188
+ TIGHTLY_COUPLED_MONOREPOS.values
189
+ .find { |deps| deps.include?(dependency.name) }
190
+ return false unless monorepo_dep_names
191
+
192
+ deps_to_update =
193
+ top_level_dependencies
194
+ .select { |d| monorepo_dep_names.include?(d.name) }
195
+
196
+ deps_to_update.count > 1
197
+ end
198
+
199
+ def updated_monorepo_dependencies
200
+ monorepo_dep_names =
201
+ TIGHTLY_COUPLED_MONOREPOS.values
202
+ .find { |deps| deps.include?(dependency.name) }
203
+
204
+ deps_to_update =
205
+ top_level_dependencies
206
+ .select { |d| monorepo_dep_names.include?(d.name) }
207
+
208
+ updates = []
209
+ deps_to_update.each do |dep|
210
+ next if git_dependency?(dep)
211
+ next if dep.version &&
212
+ version_class.new(dep.version) >= latest_allowable_version
213
+
214
+ updated_version =
215
+ latest_version_finder(dep)
216
+ .possible_versions
217
+ .find { |v| v == latest_allowable_version }
218
+ next unless updated_version
219
+
220
+ updates << {
221
+ dependency: dep,
222
+ version: updated_version,
223
+ previous_version: resolve_latest_previous_version(
224
+ dep, updated_version
225
+ )
226
+ }
227
+ end
228
+
229
+ updates
230
+ end
231
+
232
+ def types_package
233
+ @types_package ||= begin
234
+ types_package_name = PackageName.new(dependency.name).types_package_name
235
+ top_level_dependencies.find { |d| types_package_name.to_s == d.name } if types_package_name
236
+ end
237
+ end
238
+
239
+ def original_package
240
+ @original_package ||= begin
241
+ original_package_name = PackageName.new(dependency.name).library_name
242
+ top_level_dependencies.find { |d| original_package_name.to_s == d.name } if original_package_name
243
+ end
244
+ end
245
+
246
+ def latest_types_package_version
247
+ @latest_types_package_version ||= latest_version_finder(types_package).latest_version_from_registry
248
+ end
249
+
250
+ def types_update_available?
251
+ return false if types_package.nil?
252
+
253
+ return false if latest_types_package_version.nil?
254
+
255
+ return false unless latest_allowable_version.backwards_compatible_with?(latest_types_package_version)
256
+
257
+ return false unless version_class.correct?(types_package.version)
258
+
259
+ current_types_package_version = version_class.new(types_package.version)
260
+
261
+ return false unless current_types_package_version < latest_types_package_version
262
+
263
+ true
264
+ end
265
+
266
+ def original_package_update_available?
267
+ return false if original_package.nil?
268
+
269
+ return false unless version_class.correct?(original_package.version)
270
+
271
+ original_package_version = version_class.new(original_package.version)
272
+
273
+ latest_version = latest_version_finder(original_package).latest_version_from_registry
274
+
275
+ # If the latest version is within the scope of the current requirements,
276
+ # latest_version will be nil. In such cases, there is no update available.
277
+ return false if latest_version.nil?
278
+
279
+ original_package_version < latest_version
280
+ end
281
+
282
+ def updated_types_dependencies
283
+ [{
284
+ dependency: types_package,
285
+ version: latest_types_package_version,
286
+ previous_version: resolve_latest_previous_version(
287
+ types_package, latest_types_package_version
288
+ )
289
+ }]
290
+ end
291
+
292
+ def peer_dependency_errors
293
+ return @peer_dependency_errors if @peer_dependency_errors_checked
294
+
295
+ @peer_dependency_errors_checked = true
296
+
297
+ @peer_dependency_errors =
298
+ fetch_peer_dependency_errors(version: latest_allowable_version)
299
+ end
300
+
301
+ def old_peer_dependency_errors
302
+ return @old_peer_dependency_errors if @old_peer_dependency_errors_checked
303
+
304
+ @old_peer_dependency_errors_checked = true
305
+
306
+ version = version_for_dependency(dependency)
307
+
308
+ @old_peer_dependency_errors =
309
+ fetch_peer_dependency_errors(version: version)
310
+ end
311
+
312
+ def fetch_peer_dependency_errors(version:)
313
+ # TODO: Add all of the error handling that the FileUpdater does
314
+ # here (since problematic repos will be resolved here before they're
315
+ # seen by the FileUpdater)
316
+ base_dir = dependency_files.first.directory
317
+ SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
318
+ dependency_files_builder.write_temporary_dependency_files
319
+
320
+ paths_requiring_update_check.flat_map do |path|
321
+ run_checker(path: path, version: version)
322
+ end.compact
323
+ end
324
+ rescue SharedHelpers::HelperSubprocessFailed
325
+ # Fall back to allowing the version through. Whatever error
326
+ # occurred should be properly handled by the FileUpdater. We
327
+ # can slowly migrate error handling to this class over time.
328
+ []
329
+ end
330
+
331
+ sig { params(message: String).returns(T::Array[T::Hash[String, T.nilable(String)]]) }
332
+ def handle_peer_dependency_errors(message)
333
+ errors = []
334
+ if message.match?(NPM6_PEER_DEP_ERROR_REGEX)
335
+ message.scan(NPM6_PEER_DEP_ERROR_REGEX) do
336
+ errors << Regexp.last_match&.named_captures
337
+ end
338
+ elsif message.match?(NPM8_PEER_DEP_ERROR_REGEX)
339
+ message.scan(NPM8_PEER_DEP_ERROR_REGEX) do
340
+ errors << T.must(Regexp.last_match).named_captures
341
+ end
342
+ else
343
+ raise
344
+ end
345
+ errors
346
+ end
347
+
348
+ def unmet_peer_dependencies
349
+ peer_dependency_errors
350
+ .map { |captures| error_details_from_captures(captures) }
351
+ end
352
+
353
+ def old_unmet_peer_dependencies
354
+ old_peer_dependency_errors
355
+ .map { |captures| error_details_from_captures(captures) }
356
+ end
357
+
358
+ def error_details_from_captures(captures)
359
+ return {} unless captures.is_a?(Hash)
360
+
361
+ required_dep_captures = captures.fetch("required_dep")
362
+ requiring_dep_captures = captures.fetch("requiring_dep")
363
+ return {} unless required_dep_captures && requiring_dep_captures
364
+
365
+ {
366
+ requirement_name: required_dep_captures.sub(/@[^@]+$/, ""),
367
+ requirement_version: required_dep_captures.split("@").last.delete('"'),
368
+ requiring_dep_name: requiring_dep_captures.sub(/@[^@]+$/, "")
369
+ }
370
+ end
371
+
372
+ def relevant_unmet_peer_dependencies
373
+ relevant_unmet_peer_dependencies =
374
+ unmet_peer_dependencies.select do |dep|
375
+ dep[:requirement_name] == dependency.name ||
376
+ dep[:requiring_dep_name] == dependency.name
377
+ end
378
+
379
+ unless dependency_group.nil?
380
+ # Ignore unmet peer dependencies that are in the dependency group because
381
+ # the update is also updating those dependencies.
382
+ relevant_unmet_peer_dependencies.reject! do |dep|
383
+ dependency_group.dependencies.any? do |group_dep|
384
+ dep[:requirement_name] == group_dep.name ||
385
+ dep[:requiring_dep_name] == group_dep.name
386
+ end
387
+ end
388
+ end
389
+
390
+ return [] if relevant_unmet_peer_dependencies.empty?
391
+
392
+ # Prune out any pre-existing warnings
393
+ relevant_unmet_peer_dependencies.reject do |issue|
394
+ old_unmet_peer_dependencies.any? do |old_issue|
395
+ old_issue.slice(:requirement_name, :requiring_dep_name) ==
396
+ issue.slice(:requirement_name, :requiring_dep_name)
397
+ end
398
+ end
399
+ end
400
+
401
+ # rubocop:disable Metrics/PerceivedComplexity
402
+ def satisfying_versions
403
+ latest_version_finder(dependency)
404
+ .possible_versions_with_details
405
+ .select do |version, details|
406
+ next false unless satisfies_peer_reqs_on_dep?(version)
407
+ next true unless details["peerDependencies"]
408
+ next true if version == version_for_dependency(dependency)
409
+
410
+ details["peerDependencies"].all? do |dep, req|
411
+ dep = top_level_dependencies.find { |d| d.name == dep }
412
+ next false unless dep
413
+ next git_dependency?(dep) if req.include?("/")
414
+
415
+ reqs = requirement_class.requirements_array(req)
416
+ next false unless version_for_dependency(dep)
417
+
418
+ reqs.any? { |r| r.satisfied_by?(version_for_dependency(dep)) }
419
+ rescue Gem::Requirement::BadRequirementError
420
+ false
421
+ end
422
+ end
423
+ .map(&:first)
424
+ end
425
+
426
+ # rubocop:enable Metrics/PerceivedComplexity
427
+
428
+ def satisfies_peer_reqs_on_dep?(version)
429
+ newly_broken_peer_reqs_on_dep.all? do |peer_req|
430
+ req = peer_req.fetch(:requirement_version)
431
+
432
+ # Git requirements can't be satisfied by a version
433
+ next false if req.include?("/")
434
+
435
+ reqs = requirement_class.requirements_array(req)
436
+ reqs.any? { |r| r.satisfied_by?(version) }
437
+ rescue Gem::Requirement::BadRequirementError
438
+ false
439
+ end
440
+ end
441
+
442
+ def latest_version_of_dep_with_satisfied_peer_reqs(dep)
443
+ latest_version_finder(dep)
444
+ .possible_versions_with_details
445
+ .find do |version, details|
446
+ next false unless version > version_for_dependency(dep)
447
+ next true unless details["peerDependencies"]
448
+
449
+ details["peerDependencies"].all? do |peer_dep_name, req|
450
+ # Can't handle multiple peer dependencies
451
+ next false unless peer_dep_name == dependency.name
452
+ next git_dependency?(dependency) if req.include?("/")
453
+
454
+ reqs = requirement_class.requirements_array(req)
455
+
456
+ reqs.any? { |r| r.satisfied_by?(latest_allowable_version) }
457
+ rescue Gem::Requirement::BadRequirementError
458
+ false
459
+ end
460
+ end
461
+ &.first
462
+ end
463
+
464
+ def git_dependency?(dep)
465
+ # ignored_version/raise_on_ignored are irrelevant.
466
+ GitCommitChecker
467
+ .new(dependency: dep, credentials: credentials)
468
+ .git_dependency?
469
+ end
470
+
471
+ def newly_broken_peer_reqs_on_dep
472
+ relevant_unmet_peer_dependencies
473
+ .select { |dep| dep[:requirement_name] == dependency.name }
474
+ end
475
+
476
+ def newly_broken_peer_reqs_from_dep
477
+ relevant_unmet_peer_dependencies
478
+ .select { |dep| dep[:requiring_dep_name] == dependency.name }
479
+ end
480
+
481
+ def lockfiles_for_path(lockfiles:, path:)
482
+ lockfiles.select do |lockfile|
483
+ File.dirname(lockfile.name) == File.dirname(path)
484
+ end
485
+ end
486
+
487
+ def run_checker(path:, version:)
488
+ bun_lockfiles = lockfiles_for_path(lockfiles: dependency_files_builder.bun_locks, path: path)
489
+ return run_bun_checker(path: path, version: version) if bun_lockfiles.any?
490
+
491
+ root_bun_lock = dependency_files_builder.root_bun_lock
492
+ run_bun_checker(path: path, version: version) if root_bun_lock
493
+ rescue SharedHelpers::HelperSubprocessFailed => e
494
+ handle_peer_dependency_errors(e.message)
495
+ end
496
+
497
+ def run_bun_checker(path:, version:)
498
+ SharedHelpers.with_git_configured(credentials: credentials) do
499
+ Dir.chdir(path) do
500
+ Helpers.run_bun_command(
501
+ "update #{dependency.name}@#{version} --save-text-lockfile",
502
+ fingerprint: "update <dependency_name>@<version> --save-text-lockfile"
503
+ )
504
+ end
505
+ end
506
+ end
507
+
508
+ def version_install_arg(version:)
509
+ git_source = dependency.requirements.find { |req| req[:source] && req[:source][:type] == "git" }
510
+
511
+ if git_source
512
+ "#{dependency.name}@#{git_source[:source][:url]}##{version}"
513
+ else
514
+ "#{dependency.name}@#{version}"
515
+ end
516
+ end
517
+
518
+ def requirements_for_path(requirements, path)
519
+ return requirements if path.to_s == "."
520
+
521
+ requirements.filter_map do |r|
522
+ next unless r[:file].start_with?("#{path}/")
523
+
524
+ r.merge(file: r[:file].gsub(/^#{Regexp.quote("#{path}/")}/, ""))
525
+ end
526
+ end
527
+
528
+ # Top level dependencies are required in the peer dep checker
529
+ # to fetch the manifests for all top level deps which may contain
530
+ # "peerDependency" requirements
531
+ def top_level_dependencies
532
+ @top_level_dependencies ||= Bun::FileParser.new(
533
+ dependency_files: dependency_files,
534
+ source: nil,
535
+ credentials: credentials
536
+ ).parse.select(&:top_level?)
537
+ end
538
+
539
+ def paths_requiring_update_check
540
+ @paths_requiring_update_check ||=
541
+ DependencyFilesFilterer.new(
542
+ dependency_files: dependency_files,
543
+ updated_dependencies: [dependency]
544
+ ).paths_requiring_update_check
545
+ end
546
+
547
+ def dependency_files_builder
548
+ @dependency_files_builder ||=
549
+ DependencyFilesBuilder.new(
550
+ dependency: dependency,
551
+ dependency_files: dependency_files,
552
+ credentials: credentials
553
+ )
554
+ end
555
+
556
+ def version_for_dependency(dep)
557
+ return version_class.new(dep.version) if dep.version && version_class.correct?(dep.version)
558
+
559
+ dep.requirements.filter_map { |r| r[:requirement] }
560
+ .reject { |req_string| req_string.start_with?("<") }
561
+ .select { |req_string| req_string.match?(version_regex) }
562
+ .map { |req_string| req_string.match(version_regex) }
563
+ .select { |version| version_class.correct?(version.to_s) }
564
+ .map { |version| version_class.new(version.to_s) }
565
+ .max
566
+ end
567
+
568
+ def version_class
569
+ dependency.version_class
570
+ end
571
+
572
+ def requirement_class
573
+ dependency.requirement_class
574
+ end
575
+
576
+ def version_regex
577
+ Dependabot::Bun::Version::VERSION_PATTERN
578
+ end
579
+ end
580
+ end
581
+ end
582
+ end
583
+ # rubocop:enable Metrics/ClassLength