dependabot-javascript 0.296.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/lib/dependabot/bun.rb +49 -0
  3. data/lib/dependabot/javascript/bun/file_fetcher.rb +77 -0
  4. data/lib/dependabot/javascript/bun/file_parser/bun_lock.rb +156 -0
  5. data/lib/dependabot/javascript/bun/file_parser/lockfile_parser.rb +55 -0
  6. data/lib/dependabot/javascript/bun/file_parser.rb +74 -0
  7. data/lib/dependabot/javascript/bun/file_updater/lockfile_updater.rb +138 -0
  8. data/lib/dependabot/javascript/bun/file_updater.rb +75 -0
  9. data/lib/dependabot/javascript/bun/helpers.rb +72 -0
  10. data/lib/dependabot/javascript/bun/package_manager.rb +48 -0
  11. data/lib/dependabot/javascript/bun/requirement.rb +11 -0
  12. data/lib/dependabot/javascript/bun/update_checker/conflicting_dependency_resolver.rb +64 -0
  13. data/lib/dependabot/javascript/bun/update_checker/dependency_files_builder.rb +47 -0
  14. data/lib/dependabot/javascript/bun/update_checker/latest_version_finder.rb +450 -0
  15. data/lib/dependabot/javascript/bun/update_checker/library_detector.rb +76 -0
  16. data/lib/dependabot/javascript/bun/update_checker/requirements_updater.rb +203 -0
  17. data/lib/dependabot/javascript/bun/update_checker/subdependency_version_resolver.rb +144 -0
  18. data/lib/dependabot/javascript/bun/update_checker/version_resolver.rb +525 -0
  19. data/lib/dependabot/javascript/bun/update_checker/vulnerability_auditor.rb +165 -0
  20. data/lib/dependabot/javascript/bun/update_checker.rb +440 -0
  21. data/lib/dependabot/javascript/bun/version.rb +11 -0
  22. data/lib/dependabot/javascript/shared/constraint_helper.rb +359 -0
  23. data/lib/dependabot/javascript/shared/dependency_files_filterer.rb +164 -0
  24. data/lib/dependabot/javascript/shared/file_fetcher.rb +283 -0
  25. data/lib/dependabot/javascript/shared/file_parser/lockfile_parser.rb +106 -0
  26. data/lib/dependabot/javascript/shared/file_parser.rb +454 -0
  27. data/lib/dependabot/javascript/shared/file_updater/npmrc_builder.rb +394 -0
  28. data/lib/dependabot/javascript/shared/file_updater/package_json_preparer.rb +87 -0
  29. data/lib/dependabot/javascript/shared/file_updater/package_json_updater.rb +376 -0
  30. data/lib/dependabot/javascript/shared/file_updater.rb +179 -0
  31. data/lib/dependabot/javascript/shared/language.rb +45 -0
  32. data/lib/dependabot/javascript/shared/metadata_finder.rb +209 -0
  33. data/lib/dependabot/javascript/shared/native_helpers.rb +21 -0
  34. data/lib/dependabot/javascript/shared/package_manager_detector.rb +72 -0
  35. data/lib/dependabot/javascript/shared/package_name.rb +118 -0
  36. data/lib/dependabot/javascript/shared/registry_helper.rb +190 -0
  37. data/lib/dependabot/javascript/shared/registry_parser.rb +93 -0
  38. data/lib/dependabot/javascript/shared/requirement.rb +144 -0
  39. data/lib/dependabot/javascript/shared/sub_dependency_files_filterer.rb +79 -0
  40. data/lib/dependabot/javascript/shared/update_checker/dependency_files_builder.rb +87 -0
  41. data/lib/dependabot/javascript/shared/update_checker/registry_finder.rb +358 -0
  42. data/lib/dependabot/javascript/shared/version.rb +133 -0
  43. data/lib/dependabot/javascript/shared/version_selector.rb +60 -0
  44. data/lib/dependabot/javascript.rb +39 -0
  45. metadata +327 -0
@@ -0,0 +1,376 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Dependabot
5
+ module Javascript
6
+ module Shared
7
+ class FileUpdater < Dependabot::FileUpdaters::Base
8
+ class PackageJsonUpdater
9
+ extend T::Sig
10
+
11
+ LOCAL_PACKAGE = T.let([/portal:/, /file:/].freeze, T::Array[Regexp])
12
+
13
+ PATCH_PACKAGE = T.let([/patch:/].freeze, T::Array[Regexp])
14
+
15
+ sig do
16
+ params(
17
+ package_json: Dependabot::DependencyFile,
18
+ dependencies: T::Array[Dependabot::Dependency]
19
+ ).void
20
+ end
21
+ def initialize(package_json:, dependencies:)
22
+ @package_json = package_json
23
+ @dependencies = dependencies
24
+ end
25
+
26
+ sig { returns(Dependabot::DependencyFile) }
27
+ def updated_package_json
28
+ updated_file = package_json.dup
29
+ updated_file.content = updated_package_json_content
30
+ updated_file
31
+ end
32
+
33
+ private
34
+
35
+ sig { returns(Dependabot::DependencyFile) }
36
+ attr_reader :package_json
37
+
38
+ sig { returns(T::Array[Dependabot::Dependency]) }
39
+ attr_reader :dependencies
40
+
41
+ # rubocop:disable Metrics/PerceivedComplexity
42
+
43
+ sig { returns(T.nilable(String)) }
44
+ def updated_package_json_content
45
+ # checks if we are updating single dependency in package.json
46
+ unique_deps_count = dependencies.map(&:name).to_a.uniq.compact.length
47
+
48
+ dependencies.reduce(package_json.content.dup) do |content, dep|
49
+ updated_requirements(dep)&.each do |new_req|
50
+ old_req = old_requirement(dep, new_req)
51
+
52
+ new_content = update_package_json_declaration(
53
+ package_json_content: T.must(content),
54
+ dependency_name: dep.name,
55
+ old_req: old_req,
56
+ new_req: new_req
57
+ )
58
+
59
+ if Dependabot::Experiments.enabled?(:avoid_duplicate_updates_package_json) &&
60
+ (content == new_content && unique_deps_count > 1)
61
+
62
+ # (we observed that) package.json does not always contains the same dependencies compared to
63
+ # "dependencies" list, for example, dependencies object can contain same name dependency
64
+ # "dep"=> "1.0.0" and "dev" => "1.0.1" while package.json can only contain "dep" => "1.0.0",
65
+ # the other dependency is not present in package.json so we don't have to update it, this is most
66
+ # likely (as observed) a transitive dependency which only needs update in lockfile, So we avoid
67
+ # throwing exception and let the update continue.
68
+
69
+ Dependabot.logger.info("experiment: avoid_duplicate_updates_package_json.
70
+ Updating package.json for #{dep.name} ")
71
+
72
+ raise "Expected content to change!"
73
+ end
74
+
75
+ if !Dependabot::Experiments.enabled?(:avoid_duplicate_updates_package_json) && (content == new_content)
76
+ raise "Expected content to change!"
77
+ end
78
+
79
+ content = new_content
80
+ end
81
+
82
+ new_requirements(dep).each do |new_req|
83
+ old_req = old_requirement(dep, new_req)
84
+
85
+ content = update_package_json_resolutions(
86
+ package_json_content: T.must(content),
87
+ new_req: new_req,
88
+ dependency: dep,
89
+ old_req: old_req
90
+ )
91
+ end
92
+
93
+ content
94
+ end
95
+ end
96
+ # rubocop:enable Metrics/PerceivedComplexity
97
+ sig do
98
+ params(
99
+ dependency: Dependabot::Dependency,
100
+ new_requirement: T::Hash[Symbol, T.untyped]
101
+ )
102
+ .returns(T.nilable(T::Hash[Symbol, T.untyped]))
103
+ end
104
+ def old_requirement(dependency, new_requirement)
105
+ T.must(dependency.previous_requirements)
106
+ .select { |r| r[:file] == package_json.name }
107
+ .find { |r| r[:groups] == new_requirement[:groups] }
108
+ end
109
+
110
+ sig { params(dependency: Dependabot::Dependency).returns(T::Array[T::Hash[Symbol, T.untyped]]) }
111
+ def new_requirements(dependency)
112
+ dependency.requirements.select { |r| r[:file] == package_json.name }
113
+ end
114
+
115
+ sig { params(dependency: Dependabot::Dependency).returns(T.nilable(T::Array[T::Hash[Symbol, T.untyped]])) }
116
+ def updated_requirements(dependency)
117
+ return unless dependency.previous_requirements
118
+
119
+ preliminary_check_for_update(dependency)
120
+
121
+ updated_requirement_pairs =
122
+ dependency.requirements.zip(T.must(dependency.previous_requirements))
123
+ .reject do |new_req, old_req|
124
+ next true if new_req == old_req
125
+ next false unless old_req&.fetch(:source).nil?
126
+
127
+ new_req[:requirement] == old_req&.fetch(:requirement)
128
+ end
129
+
130
+ updated_requirement_pairs
131
+ .map(&:first)
132
+ .select { |r| r[:file] == package_json.name }
133
+ end
134
+
135
+ sig do
136
+ params(
137
+ package_json_content: String,
138
+ new_req: T::Hash[Symbol, T.untyped],
139
+ dependency_name: String,
140
+ old_req: T.nilable(T::Hash[Symbol, T.untyped])
141
+ )
142
+ .returns(String)
143
+ end
144
+ def update_package_json_declaration(package_json_content:, new_req:, dependency_name:, old_req:)
145
+ original_line = declaration_line(
146
+ dependency_name: dependency_name,
147
+ dependency_req: old_req,
148
+ content: package_json_content
149
+ )
150
+
151
+ replacement_line = replacement_declaration_line(
152
+ original_line: original_line,
153
+ old_req: old_req,
154
+ new_req: new_req
155
+ )
156
+
157
+ groups = new_req.fetch(:groups)
158
+
159
+ update_package_json_sections(
160
+ groups,
161
+ package_json_content,
162
+ original_line,
163
+ replacement_line
164
+ )
165
+ end
166
+
167
+ # For full details on how Yarn resolutions work, see
168
+ # https://github.com/yarnpkg/rfcs/blob/master/implemented/
169
+ # 0000-selective-versions-resolutions.md
170
+ sig do
171
+ params(
172
+ package_json_content: String,
173
+ new_req: T::Hash[Symbol, T.untyped],
174
+ dependency: Dependabot::Dependency,
175
+ old_req: T.nilable(T::Hash[Symbol, T.untyped])
176
+ )
177
+ .returns(String)
178
+ end
179
+ def update_package_json_resolutions(package_json_content:, new_req:, dependency:, old_req:)
180
+ dep = dependency
181
+ parsed_json_content = JSON.parse(package_json_content)
182
+ resolutions =
183
+ parsed_json_content.fetch("resolutions", parsed_json_content.dig("pnpm", "overrides") || {})
184
+ .reject { |_, v| v != old_req && v != dep.previous_version }
185
+ .select { |k, _| k == dep.name || k.end_with?("/#{dep.name}") }
186
+
187
+ return package_json_content unless resolutions.any?
188
+
189
+ content = package_json_content
190
+ resolutions.each do |_, resolution|
191
+ original_line = declaration_line(
192
+ dependency_name: dep.name,
193
+ dependency_req: { requirement: resolution },
194
+ content: content
195
+ )
196
+
197
+ new_resolution = resolution == old_req ? new_req : dep.version
198
+
199
+ replacement_line = replacement_declaration_line(
200
+ original_line: original_line,
201
+ old_req: { requirement: resolution },
202
+ new_req: { requirement: new_resolution }
203
+ )
204
+
205
+ content = update_package_json_sections(
206
+ %w(resolutions overrides), content, original_line, replacement_line
207
+ )
208
+ end
209
+ content
210
+ end
211
+
212
+ sig do
213
+ params(
214
+ dependency_name: String,
215
+ dependency_req: T.nilable(T::Hash[Symbol, T.untyped]),
216
+ content: String
217
+ )
218
+ .returns(String)
219
+ end
220
+ def declaration_line(dependency_name:, dependency_req:, content:)
221
+ git_dependency = dependency_req&.dig(:source, :type) == "git"
222
+
223
+ unless git_dependency
224
+ requirement = dependency_req&.fetch(:requirement)
225
+ return content.match(/"#{Regexp.escape(dependency_name)}"\s*:\s*
226
+ "#{Regexp.escape(requirement)}"/x).to_s
227
+ end
228
+
229
+ username, repo =
230
+ dependency_req&.dig(:source, :url)&.split("/")&.last(2)
231
+
232
+ content.match(
233
+ %r{"#{Regexp.escape(dependency_name)}"\s*:\s*
234
+ ".*?#{Regexp.escape(username)}/#{Regexp.escape(repo)}.*"}x
235
+ ).to_s
236
+ end
237
+
238
+ sig do
239
+ params(
240
+ original_line: String,
241
+ old_req: T.nilable(T::Hash[Symbol, T.untyped]),
242
+ new_req: T::Hash[Symbol, T.untyped]
243
+ )
244
+ .returns(String)
245
+ end
246
+ def replacement_declaration_line(original_line:, old_req:, new_req:)
247
+ was_git_dependency = old_req&.dig(:source, :type) == "git"
248
+ now_git_dependency = new_req.dig(:source, :type) == "git"
249
+
250
+ unless was_git_dependency
251
+ return original_line.gsub(
252
+ %("#{old_req&.fetch(:requirement)}"),
253
+ %("#{new_req.fetch(:requirement)}")
254
+ )
255
+ end
256
+
257
+ unless now_git_dependency
258
+ return original_line.gsub(
259
+ /(?<=\s").*[^\\](?=")/,
260
+ new_req.fetch(:requirement)
261
+ )
262
+ end
263
+
264
+ if original_line.match?(/#[\^~=<>]|semver:/)
265
+ return update_git_semver_requirement(
266
+ original_line: original_line,
267
+ old_req: old_req,
268
+ new_req: new_req
269
+ )
270
+ end
271
+
272
+ original_line.gsub(
273
+ %(##{old_req&.dig(:source, :ref)}"),
274
+ %(##{new_req.dig(:source, :ref)}")
275
+ )
276
+ end
277
+
278
+ sig do
279
+ params(
280
+ original_line: String,
281
+ old_req: T.nilable(T::Hash[Symbol, String]),
282
+ new_req: T::Hash[Symbol, String]
283
+ )
284
+ .returns(String)
285
+ end
286
+ def update_git_semver_requirement(original_line:, old_req:, new_req:)
287
+ if original_line.include?("semver:")
288
+ return original_line.gsub(
289
+ %(semver:#{old_req&.fetch(:requirement)}"),
290
+ %(semver:#{new_req.fetch(:requirement)}")
291
+ )
292
+ end
293
+
294
+ raise "Not a semver req!" unless original_line.match?(/#[\^~=<>]/)
295
+
296
+ original_line.gsub(
297
+ %(##{old_req&.fetch(:requirement)}"),
298
+ %(##{new_req.fetch(:requirement)}")
299
+ )
300
+ end
301
+
302
+ sig do
303
+ params(
304
+ sections: T::Array[String],
305
+ content: String,
306
+ old_line: String,
307
+ new_line: String
308
+ )
309
+ .returns(String)
310
+ end
311
+ def update_package_json_sections(sections, content, old_line, new_line)
312
+ # Currently, Dependabot doesn't update peerDependencies. However,
313
+ # if a development dependency is being updated and its requirement
314
+ # matches the requirement on a peer dependency we probably want to
315
+ # update the peer too.
316
+ #
317
+ # TODO: Move this logic to the UpdateChecker (and parse peer deps)
318
+ sections += ["peerDependencies"]
319
+ sections_regex = /#{sections.join('|')}/
320
+
321
+ declaration_blocks = T.let([], T::Array[String])
322
+
323
+ content.scan(/['"]#{sections_regex}['"]\s*:\s*\{/m) do
324
+ mtch = T.must(Regexp.last_match)
325
+ declaration_blocks <<
326
+ (mtch.to_s + T.must(mtch.post_match[0..closing_bracket_index(mtch.post_match)]))
327
+ end
328
+
329
+ declaration_blocks.reduce(content.dup) do |new_content, block|
330
+ updated_block = block.sub(old_line, new_line)
331
+ new_content.sub(block, updated_block)
332
+ end
333
+ end
334
+
335
+ sig { params(string: String).returns(Integer) }
336
+ def closing_bracket_index(string)
337
+ closes_required = 1
338
+
339
+ string.chars.each_with_index do |char, index|
340
+ closes_required += 1 if char == "{"
341
+ closes_required -= 1 if char == "}"
342
+ return index if closes_required.zero?
343
+ end
344
+
345
+ 0
346
+ end
347
+
348
+ sig { params(dependency: Dependabot::Dependency).void }
349
+ def preliminary_check_for_update(dependency)
350
+ T.must(dependency.previous_requirements).each do |req, _dep|
351
+ next if req.fetch(:requirement).nil?
352
+
353
+ # some deps are patched with local patches, we don't need to update them
354
+ if req.fetch(:requirement).match?(Regexp.union(PATCH_PACKAGE))
355
+ Dependabot.logger.info("Func: updated_requirements. dependency patched #{dependency.name}," \
356
+ " Requirement: '#{req.fetch(:requirement)}'")
357
+
358
+ raise DependencyFileNotResolvable,
359
+ "Dependency is patched locally, Update not required."
360
+ end
361
+
362
+ # some deps are added as local packages, we don't need to update them as they are referred to a local path
363
+ next unless req.fetch(:requirement).match?(Regexp.union(LOCAL_PACKAGE))
364
+
365
+ Dependabot.logger.info("Func: updated_requirements. local package #{dependency.name}," \
366
+ " Requirement: '#{req.fetch(:requirement)}'")
367
+
368
+ raise DependencyFileNotResolvable,
369
+ "Local package, Update not required."
370
+ end
371
+ end
372
+ end
373
+ end
374
+ end
375
+ end
376
+ end
@@ -0,0 +1,179 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Dependabot
5
+ module Javascript
6
+ module Shared
7
+ class FileUpdater < Dependabot::FileUpdaters::Base
8
+ extend T::Sig
9
+
10
+ abstract!
11
+
12
+ class NoChangeError < StandardError
13
+ extend T::Sig
14
+
15
+ sig { params(message: String, error_context: T::Hash[Symbol, T.untyped]).void }
16
+ def initialize(message:, error_context:)
17
+ super(message)
18
+ @error_context = error_context
19
+ end
20
+
21
+ sig { returns(T::Hash[Symbol, T.untyped]) }
22
+ def sentry_context
23
+ { extra: @error_context }
24
+ end
25
+ end
26
+
27
+ sig { override.returns(T::Array[DependencyFile]) }
28
+ def updated_dependency_files
29
+ updated_files = T.let([], T::Array[DependencyFile])
30
+
31
+ updated_files += updated_manifest_files
32
+ updated_files += updated_lockfiles
33
+
34
+ if updated_files.none?
35
+ raise NoChangeError.new(
36
+ message: "No files were updated!",
37
+ error_context: error_context(updated_files: updated_files)
38
+ )
39
+ end
40
+
41
+ sorted_updated_files = updated_files.sort_by(&:name)
42
+ if sorted_updated_files == filtered_dependency_files.sort_by(&:name)
43
+ raise NoChangeError.new(
44
+ message: "Updated files are unchanged!",
45
+ error_context: error_context(updated_files: updated_files)
46
+ )
47
+ end
48
+
49
+ vendor_updated_files(updated_files)
50
+ end
51
+
52
+ private
53
+
54
+ sig do
55
+ params(updated_files: T::Array[Dependabot::DependencyFile]).returns(T::Array[Dependabot::DependencyFile])
56
+ end
57
+ def vendor_updated_files(updated_files)
58
+ base_dir = T.must(updated_files.first).directory
59
+ T.unsafe(vendor_updater).updated_vendor_cache_files(base_directory: base_dir).each do |file|
60
+ updated_files << file
61
+ end
62
+ install_state_updater.updated_files(base_directory: base_dir).each do |file|
63
+ updated_files << file
64
+ end
65
+
66
+ updated_files
67
+ end
68
+
69
+ # Dynamically fetch the vendor cache folder from yarn
70
+ sig { returns(String) }
71
+ def vendor_cache_dir
72
+ @vendor_cache_dir ||= T.let(
73
+ "./.yarn/cache",
74
+ T.nilable(String)
75
+ )
76
+ end
77
+
78
+ sig { returns(String) }
79
+ def install_state_path
80
+ @install_state_path ||= T.let(
81
+ "./.yarn/install-state.gz",
82
+ T.nilable(String)
83
+ )
84
+ end
85
+
86
+ sig { returns(Dependabot::FileUpdaters::VendorUpdater) }
87
+ def vendor_updater
88
+ Dependabot::FileUpdaters::VendorUpdater.new(
89
+ repo_contents_path: repo_contents_path,
90
+ vendor_dir: vendor_cache_dir
91
+ )
92
+ end
93
+
94
+ sig { returns(Dependabot::FileUpdaters::ArtifactUpdater) }
95
+ def install_state_updater
96
+ Dependabot::FileUpdaters::ArtifactUpdater.new(
97
+ repo_contents_path: repo_contents_path,
98
+ target_directory: install_state_path
99
+ )
100
+ end
101
+
102
+ sig { returns(Dependabot::FileUpdaters::ArtifactUpdater) }
103
+ def pnp_updater
104
+ Dependabot::FileUpdaters::ArtifactUpdater.new(
105
+ repo_contents_path: repo_contents_path,
106
+ target_directory: "./"
107
+ )
108
+ end
109
+
110
+ sig { returns(T::Array[DependencyFile]) }
111
+ def filtered_dependency_files
112
+ @filtered_dependency_files ||= T.let(
113
+ if dependencies.any?(&:top_level?)
114
+ Shared::DependencyFilesFilterer.new(
115
+ dependency_files: dependency_files,
116
+ updated_dependencies: dependencies,
117
+ lockfile_parser_class: lockfile_parser_class
118
+ ).files_requiring_update
119
+ else
120
+ Shared::SubDependencyFilesFilterer.new(
121
+ dependency_files: dependency_files,
122
+ updated_dependencies: dependencies
123
+ ).files_requiring_update
124
+ end, T.nilable(T::Array[DependencyFile])
125
+ )
126
+ end
127
+
128
+ sig { abstract.returns(T.class_of(FileParser::LockfileParser)) }
129
+ def lockfile_parser_class; end
130
+
131
+ sig { override.void }
132
+ def check_required_files
133
+ raise DependencyFileNotFound.new(nil, "package.json not found.") unless get_original_file("package.json")
134
+ end
135
+
136
+ sig { params(updated_files: T::Array[DependencyFile]).returns(T::Hash[Symbol, T.untyped]) }
137
+ def error_context(updated_files:)
138
+ {
139
+ dependencies: dependencies.map(&:to_h),
140
+ updated_files: updated_files.map(&:name),
141
+ dependency_files: dependency_files.map(&:name)
142
+ }
143
+ end
144
+
145
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
146
+ def package_files
147
+ @package_files ||= T.let(
148
+ filtered_dependency_files.select do |f|
149
+ f.name.end_with?("package.json")
150
+ end, T.nilable(T::Array[DependencyFile])
151
+ )
152
+ end
153
+
154
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
155
+ def updated_manifest_files
156
+ package_files.filter_map do |file|
157
+ updated_content = updated_package_json_content(file)
158
+ next if updated_content == file.content
159
+
160
+ updated_file(file: file, content: T.must(updated_content))
161
+ end
162
+ end
163
+
164
+ sig { abstract.returns(T::Array[Dependabot::DependencyFile]) }
165
+ def updated_lockfiles; end
166
+
167
+ sig { params(file: Dependabot::DependencyFile).returns(T.nilable(String)) }
168
+ def updated_package_json_content(file)
169
+ @updated_package_json_content ||= T.let({}, T.nilable(T::Hash[String, T.nilable(String)]))
170
+ @updated_package_json_content[file.name] ||=
171
+ PackageJsonUpdater.new(
172
+ package_json: file,
173
+ dependencies: dependencies
174
+ ).updated_package_json.content
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,45 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ module Dependabot
5
+ module Javascript
6
+ module Shared
7
+ class Language < Ecosystem::VersionManager
8
+ extend T::Sig
9
+ NAME = "javascript"
10
+
11
+ SUPPORTED_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version])
12
+
13
+ DEPRECATED_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version])
14
+
15
+ sig do
16
+ params(
17
+ detected_version: T.nilable(String),
18
+ raw_version: T.nilable(String),
19
+ requirement: T.nilable(Requirement)
20
+ ).void
21
+ end
22
+ def initialize(detected_version: nil, raw_version: nil, requirement: nil)
23
+ super(
24
+ name: NAME,
25
+ detected_version: detected_version ? Version.new(detected_version) : nil,
26
+ version: raw_version ? Version.new(raw_version) : nil,
27
+ deprecated_versions: DEPRECATED_VERSIONS,
28
+ supported_versions: SUPPORTED_VERSIONS,
29
+ requirement: requirement
30
+ )
31
+ end
32
+
33
+ sig { override.returns(T::Boolean) }
34
+ def deprecated?
35
+ false
36
+ end
37
+
38
+ sig { override.returns(T::Boolean) }
39
+ def unsupported?
40
+ false
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end