dependabot-npm_and_yarn 0.217.0 → 0.218.0

Sign up to get free protection for your applications and to get access to all the features.
data/helpers/package.json CHANGED
@@ -14,6 +14,8 @@
14
14
  "detect-indent": "^6.1.0",
15
15
  "nock": "^13.3.0",
16
16
  "npm": "6.14.18",
17
+ "@pnpm/lockfile-file": "^8.0.2",
18
+ "@pnpm/dependency-path": "^2.1.1",
17
19
  "semver": "^7.4.0"
18
20
  },
19
21
  "devDependencies": {
@@ -14,6 +14,10 @@ module Dependabot
14
14
  @updated_dependencies = updated_dependencies
15
15
  end
16
16
 
17
+ def paths_requiring_update_check
18
+ @paths_requiring_update_check ||= fetch_paths_requiring_update_check
19
+ end
20
+
17
21
  def files_requiring_update
18
22
  @files_requiring_update ||=
19
23
  dependency_files.select do |file|
@@ -34,6 +38,15 @@ module Dependabot
34
38
 
35
39
  attr_reader :dependency_files, :updated_dependencies
36
40
 
41
+ def fetch_paths_requiring_update_check
42
+ # if only a root lockfile exists, it tracks all dependencies
43
+ return [File.dirname(root_lockfile.name)] if lockfiles == [root_lockfile]
44
+
45
+ package_files_requiring_update.map do |file|
46
+ File.dirname(file.name)
47
+ end
48
+ end
49
+
37
50
  def dependency_manifest_requirements
38
51
  @dependency_manifest_requirements ||=
39
52
  updated_dependencies.flat_map do |dep|
@@ -50,12 +63,29 @@ module Dependabot
50
63
  end
51
64
 
52
65
  def workspaces_lockfile?(lockfile)
53
- return false unless ["yarn.lock", "package-lock.json"].include?(lockfile.name)
54
- return false unless parsed_root_package_json["workspaces"]
66
+ return false unless ["yarn.lock", "package-lock.json", "pnpm-lock.yaml"].include?(lockfile.name)
67
+
68
+ return false unless parsed_root_package_json["workspaces"] || dependency_files.any? do |file|
69
+ file.name.end_with?("pnpm-workspace.yaml") && File.dirname(file.name) == File.dirname(lockfile.name)
70
+ end
55
71
 
56
72
  updated_dependencies_in_lockfile?(lockfile)
57
73
  end
58
74
 
75
+ def root_lockfile
76
+ @root_lockfile ||=
77
+ lockfiles.find do |file|
78
+ File.dirname(file.name) == "."
79
+ end
80
+ end
81
+
82
+ def lockfiles
83
+ @lockfiles ||=
84
+ dependency_files.select do |file|
85
+ lockfile?(file)
86
+ end
87
+ end
88
+
59
89
  def parsed_root_package_json
60
90
  @parsed_root_package_json ||=
61
91
  begin
@@ -88,6 +118,7 @@ module Dependabot
88
118
  file.name.end_with?(
89
119
  "package-lock.json",
90
120
  "yarn.lock",
121
+ "pnpm-lock.yaml",
91
122
  "npm-shrinkwrap.json"
92
123
  )
93
124
  end
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "json"
4
+ require "dependabot/experiments"
4
5
  require "dependabot/logger"
5
6
  require "dependabot/file_fetchers"
6
7
  require "dependabot/file_fetchers/base"
7
8
  require "dependabot/npm_and_yarn/helpers"
9
+ require "dependabot/npm_and_yarn/package_manager"
8
10
  require "dependabot/npm_and_yarn/file_parser"
9
11
  require "dependabot/npm_and_yarn/file_parser/lockfile_parser"
10
12
 
@@ -55,6 +57,7 @@ module Dependabot
55
57
 
56
58
  package_managers["npm"] = Helpers.npm_version_numeric(package_lock.content) if package_lock
57
59
  package_managers["yarn"] = yarn_version if yarn_version
60
+ package_managers["pnpm"] = pnpm_version if pnpm_version
58
61
  package_managers["shrinkwrap"] = 1 if shrinkwrap
59
62
  package_managers["unknown"] = 1 if package_managers.empty?
60
63
 
@@ -71,6 +74,7 @@ module Dependabot
71
74
  fetched_files << package_json
72
75
  fetched_files += npm_files
73
76
  fetched_files += yarn_files
77
+ fetched_files += pnpm_files if Experiments.enabled?(:pnpm_updates)
74
78
  fetched_files += lerna_files
75
79
  fetched_files += workspace_package_jsons
76
80
  fetched_files += path_dependencies(fetched_files)
@@ -80,7 +84,7 @@ module Dependabot
80
84
 
81
85
  def npm_files
82
86
  fetched_npm_files = []
83
- fetched_npm_files << package_lock if package_lock && !ignore_package_lock?
87
+ fetched_npm_files << package_lock if package_lock
84
88
  fetched_npm_files << shrinkwrap if shrinkwrap
85
89
  fetched_npm_files << npmrc if npmrc
86
90
  fetched_npm_files << inferred_npmrc if inferred_npmrc
@@ -95,6 +99,14 @@ module Dependabot
95
99
  fetched_yarn_files
96
100
  end
97
101
 
102
+ def pnpm_files
103
+ fetched_pnpm_files = []
104
+ fetched_pnpm_files << pnpm_lock if pnpm_lock
105
+ fetched_pnpm_files << pnpm_workspace_yaml if pnpm_workspace_yaml
106
+ fetched_pnpm_files += pnpm_workspace_package_jsons
107
+ fetched_pnpm_files
108
+ end
109
+
98
110
  def lerna_files
99
111
  fetched_lerna_files = []
100
112
  fetched_lerna_files << lerna_json if lerna_json
@@ -152,17 +164,29 @@ module Dependabot
152
164
  def yarn_version
153
165
  return @yarn_version if defined?(@yarn_version)
154
166
 
155
- package = JSON.parse(package_json.content)
156
- if (package_manager = package.fetch("packageManager", nil))
157
- get_yarn_version_from_package_json(package_manager)
158
- elsif yarn_lock
159
- Helpers.yarn_version_numeric(yarn_lock)
160
- end
167
+ @yarn_version = package_manager.locked_version("yarn") || guess_yarn_version
161
168
  end
162
169
 
163
- def get_yarn_version_from_package_json(package_manager)
164
- version_match = package_manager.match(/yarn@(?<version>\d+.\d+.\d+)/)
165
- version_match&.named_captures&.fetch("version", nil)
170
+ def guess_yarn_version
171
+ return unless yarn_lock
172
+
173
+ Helpers.yarn_version_numeric(yarn_lock)
174
+ end
175
+
176
+ def pnpm_version
177
+ return @pnpm_version if defined?(@pnpm_version)
178
+
179
+ @pnpm_version = package_manager.locked_version("pnpm") || guess_pnpm_version
180
+ end
181
+
182
+ def guess_pnpm_version
183
+ return unless pnpm_lock
184
+
185
+ Helpers.pnpm_major_version
186
+ end
187
+
188
+ def package_manager
189
+ @package_manager ||= PackageManager.new(parsed_package_json)
166
190
  end
167
191
 
168
192
  def package_json
@@ -170,15 +194,27 @@ module Dependabot
170
194
  end
171
195
 
172
196
  def package_lock
173
- @package_lock ||= fetch_file_if_present("package-lock.json")
197
+ return @package_lock if defined?(@package_lock)
198
+
199
+ @package_lock = fetch_file_if_present("package-lock.json") unless skip_package_lock?
174
200
  end
175
201
 
176
202
  def yarn_lock
177
- @yarn_lock ||= fetch_file_if_present("yarn.lock")
203
+ return @yarn_lock if defined?(@yarn_lock)
204
+
205
+ @yarn_lock = fetch_file_if_present("yarn.lock")
206
+ end
207
+
208
+ def pnpm_lock
209
+ return @pnpm_lock if defined?(@pnpm_lock)
210
+
211
+ @pnpm_lock = fetch_file_if_present("pnpm-lock.yaml") unless skip_pnpm_lock?
178
212
  end
179
213
 
180
214
  def shrinkwrap
181
- @shrinkwrap ||= fetch_file_if_present("npm-shrinkwrap.json")
215
+ return @shrinkwrap if defined?(@shrinkwrap)
216
+
217
+ @shrinkwrap = fetch_file_if_present("npm-shrinkwrap.json")
182
218
  end
183
219
 
184
220
  def npmrc
@@ -224,6 +260,11 @@ module Dependabot
224
260
  tap { |f| f.support_file = true }
225
261
  end
226
262
 
263
+ def pnpm_workspace_yaml
264
+ @pnpm_workspace_yaml ||= fetch_file_if_present("pnpm-workspace.yaml")&.
265
+ tap { |f| f.support_file = true }
266
+ end
267
+
227
268
  def lerna_json
228
269
  @lerna_json ||= fetch_file_if_present("lerna.json")&.
229
270
  tap { |f| f.support_file = true }
@@ -237,6 +278,10 @@ module Dependabot
237
278
  @lerna_packages ||= fetch_lerna_packages
238
279
  end
239
280
 
281
+ def pnpm_workspace_package_jsons
282
+ @pnpm_workspace_package_jsons ||= fetch_pnpm_workspace_package_jsons
283
+ end
284
+
240
285
  # rubocop:disable Metrics/PerceivedComplexity
241
286
  def path_dependencies(fetched_files)
242
287
  package_json_files = []
@@ -361,50 +406,36 @@ module Dependabot
361
406
  def fetch_workspace_package_jsons
362
407
  return [] unless parsed_package_json["workspaces"]
363
408
 
364
- package_json_files = []
365
-
366
- workspace_paths(parsed_package_json["workspaces"]).each do |workspace|
367
- file = File.join(workspace, "package.json")
368
-
369
- begin
370
- package_json_files << fetch_file_from_host(file)
371
- rescue Dependabot::DependencyFileNotFound
372
- nil
373
- end
409
+ workspace_paths(parsed_package_json["workspaces"]).filter_map do |workspace|
410
+ fetch_package_json_if_present(workspace)
374
411
  end
375
-
376
- package_json_files
377
412
  end
378
413
 
379
414
  def fetch_lerna_packages
380
415
  return [] unless parsed_lerna_json["packages"]
381
416
 
382
- dependency_files = []
417
+ workspace_paths(parsed_lerna_json["packages"]).flat_map do |workspace|
418
+ fetch_lerna_packages_from_path(workspace)
419
+ end.compact
420
+ end
383
421
 
384
- workspace_paths(parsed_lerna_json["packages"]).each do |workspace|
385
- dependency_files += fetch_lerna_packages_from_path(workspace)
386
- end
422
+ def fetch_pnpm_workspace_package_jsons
423
+ return [] unless parsed_pnpm_workspace_yaml["packages"]
387
424
 
388
- dependency_files
425
+ workspace_paths(parsed_pnpm_workspace_yaml["packages"]).filter_map do |workspace|
426
+ fetch_package_json_if_present(workspace)
427
+ end
389
428
  end
390
429
 
391
430
  def fetch_lerna_packages_from_path(path)
392
- dependency_files = []
393
-
394
- package_json_path = File.join(path, "package.json")
431
+ package_json = fetch_package_json_if_present(path)
432
+ return unless package_json
395
433
 
396
- begin
397
- dependency_files << fetch_file_from_host(package_json_path)
398
- dependency_files += [
399
- fetch_file_if_present(File.join(path, "package-lock.json")),
400
- fetch_file_if_present(File.join(path, "yarn.lock")),
401
- fetch_file_if_present(File.join(path, "npm-shrinkwrap.json"))
402
- ].compact
403
- rescue Dependabot::DependencyFileNotFound
404
- nil
405
- end
406
-
407
- dependency_files
434
+ [package_json] + [
435
+ fetch_file_if_present(File.join(path, "package-lock.json")),
436
+ fetch_file_if_present(File.join(path, "yarn.lock")),
437
+ fetch_file_if_present(File.join(path, "npm-shrinkwrap.json"))
438
+ ]
408
439
  end
409
440
 
410
441
  def workspace_paths(workspace_object)
@@ -466,6 +497,18 @@ module Dependabot
466
497
  matching_paths(prefix + glob, paths)
467
498
  end
468
499
 
500
+ def fetch_package_json_if_present(workspace)
501
+ file = File.join(workspace, "package.json")
502
+
503
+ begin
504
+ fetch_file_from_host(file)
505
+ rescue Dependabot::DependencyFileNotFound
506
+ # Not all paths matched by a workspace glob may contain a package.json
507
+ # file. Ignore if that's the case
508
+ nil
509
+ end
510
+ end
511
+
469
512
  # The packages/!(not-this-package) syntax is unique to Yarn
470
513
  def yarn_ignored_glob(glob)
471
514
  glob.match?(/!\(.*?\)/) && glob.gsub(/(!\((.*?)\))/, '\2')
@@ -493,12 +536,26 @@ module Dependabot
493
536
  {}
494
537
  end
495
538
 
496
- def ignore_package_lock?
539
+ def parsed_pnpm_workspace_yaml
540
+ return {} unless pnpm_workspace_yaml
541
+
542
+ YAML.safe_load(pnpm_workspace_yaml.content)
543
+ rescue Pysch::SyntaxError
544
+ raise Dependabot::DependencyFileNotParseable, pnpm_workspace_yaml.path
545
+ end
546
+
547
+ def skip_package_lock?
497
548
  return false unless npmrc
498
549
 
499
550
  npmrc.content.match?(/^package-lock\s*=\s*false/)
500
551
  end
501
552
 
553
+ def skip_pnpm_lock?
554
+ return false unless npmrc
555
+
556
+ npmrc.content.match?(/^lockfile\s*=\s*false/)
557
+ end
558
+
502
559
  def build_unfetchable_deps(unfetchable_deps)
503
560
  return [] unless package_lock || yarn_lock
504
561
 
@@ -9,6 +9,7 @@ module Dependabot
9
9
  class FileParser
10
10
  class LockfileParser
11
11
  require "dependabot/npm_and_yarn/file_parser/yarn_lock"
12
+ require "dependabot/npm_and_yarn/file_parser/pnpm_lock"
12
13
  require "dependabot/npm_and_yarn/file_parser/json_lock"
13
14
 
14
15
  def initialize(dependency_files:)
@@ -22,7 +23,7 @@ module Dependabot
22
23
  # end up unique by name. That's not a perfect representation of
23
24
  # the nested nature of JS resolution, but it makes everything work
24
25
  # comparably to other flat-resolution strategies
25
- (yarn_locks + package_locks + shrinkwraps).each do |file|
26
+ (yarn_locks + pnpm_locks + package_locks + shrinkwraps).each do |file|
26
27
  dependency_set += lockfile_for(file).dependencies
27
28
  end
28
29
 
@@ -50,10 +51,10 @@ module Dependabot
50
51
  def potential_lockfiles_for_manifest(manifest_filename)
51
52
  dir_name = File.dirname(manifest_filename)
52
53
  possible_lockfile_names =
53
- %w(package-lock.json npm-shrinkwrap.json yarn.lock).map do |f|
54
+ %w(package-lock.json npm-shrinkwrap.json pnpm-lock.yaml yarn.lock).map do |f|
54
55
  Pathname.new(File.join(dir_name, f)).cleanpath.to_path
55
56
  end +
56
- %w(yarn.lock package-lock.json npm-shrinkwrap.json)
57
+ %w(yarn.lock pnpm-lock.yaml package-lock.json npm-shrinkwrap.json)
57
58
 
58
59
  possible_lockfile_names.uniq.
59
60
  filter_map { |nm| dependency_files.find { |f| f.name == nm } }
@@ -67,8 +68,10 @@ module Dependabot
67
68
  @lockfiles ||= {}
68
69
  @lockfiles[file.name] ||= if [*package_locks, *shrinkwraps].include?(file)
69
70
  JsonLock.new(file)
70
- else
71
+ elsif yarn_locks.include?(file)
71
72
  YarnLock.new(file)
73
+ else
74
+ PnpmLock.new(file)
72
75
  end
73
76
  end
74
77
 
@@ -78,6 +81,12 @@ module Dependabot
78
81
  select { |f| f.name.end_with?("package-lock.json") }
79
82
  end
80
83
 
84
+ def pnpm_locks
85
+ @pnpm_locks ||=
86
+ dependency_files.
87
+ select { |f| f.name.end_with?("pnpm-lock.yaml") }
88
+ end
89
+
81
90
  def yarn_locks
82
91
  @yarn_locks ||=
83
92
  dependency_files.
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dependabot/errors"
4
+
5
+ module Dependabot
6
+ module NpmAndYarn
7
+ class FileParser
8
+ class PnpmLock
9
+ def initialize(dependency_file)
10
+ @dependency_file = dependency_file
11
+ end
12
+
13
+ def parsed
14
+ @parsed ||= SharedHelpers.in_a_temporary_directory do
15
+ File.write("pnpm-lock.yaml", @dependency_file.content)
16
+
17
+ SharedHelpers.run_helper_subprocess(
18
+ command: NativeHelpers.helper_path,
19
+ function: "pnpm:parseLockfile",
20
+ args: [Dir.pwd]
21
+ )
22
+ rescue SharedHelpers::HelperSubprocessFailed
23
+ raise Dependabot::DependencyFileNotParseable, @dependency_file.path
24
+ end
25
+ end
26
+
27
+ def dependencies
28
+ dependency_set = Dependabot::NpmAndYarn::FileParser::DependencySet.new
29
+
30
+ parsed.each do |details|
31
+ next if details["aliased"]
32
+
33
+ name = details["name"]
34
+ version = details["version"]
35
+
36
+ dependency_args = {
37
+ name: name,
38
+ version: version,
39
+ package_manager: "npm_and_yarn",
40
+ requirements: []
41
+ }
42
+
43
+ if details["dev"]
44
+ dependency_args[:subdependency_metadata] =
45
+ [{ production: !details["dev"] }]
46
+ end
47
+
48
+ dependency_set << Dependency.new(**dependency_args)
49
+ end
50
+
51
+ dependency_set
52
+ end
53
+
54
+ def details(dependency_name, requirement, _manifest_name)
55
+ details_candidates = parsed.select { |info| info["name"] == dependency_name }
56
+
57
+ # If there's only one entry for this dependency, use it, even if
58
+ # the requirement in the lockfile doesn't match
59
+ if details_candidates.one?
60
+ details_candidates.first
61
+ else
62
+ details_candidates.find { |info| info["specifiers"]&.include?(requirement) }
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -418,6 +418,12 @@ module Dependabot
418
418
  raise Dependabot::DependencyFileNotParseable, msg
419
419
  end
420
420
 
421
+ if error_message.include?("EBADENGINE")
422
+ msg = "Dependabot uses Node.js #{`node --version`} and NPM #{`npm --version`}. " \
423
+ "Due to the engine-strict setting, the update will not succeed."
424
+ raise Dependabot::DependencyFileNotResolvable, msg
425
+ end
426
+
421
427
  raise error
422
428
  end
423
429
  # rubocop:enable Metrics/AbcSize
@@ -81,7 +81,9 @@ module Dependabot
81
81
  next false if CENTRAL_REGISTRIES.include?(cred["registry"])
82
82
 
83
83
  # If all the URLs include this registry, it's global
84
- next true if dependency_urls.all? { |url| url.include?(cred["registry"]) }
84
+ next true if dependency_urls.size.positive? && dependency_urls.all? do |url|
85
+ url.include?(cred["registry"])
86
+ end
85
87
 
86
88
  # Check if this registry has already been defined in .npmrc as a scoped registry
87
89
  next false if npmrc_scoped_registries.any? { |sr| sr.include?(cred["registry"]) }
@@ -133,8 +135,8 @@ module Dependabot
133
135
  @dependency_urls = []
134
136
  if package_lock
135
137
  @dependency_urls +=
136
- parsed_package_lock.fetch("dependencies", {}).
137
- filter_map { |_, details| details["resolved"] }.
138
+ package_lock.content.scan(/"resolved"\s*:\s*"(.*)"/).
139
+ flatten.
138
140
  select { |url| url.is_a?(String) }.
139
141
  reject { |url| url.start_with?("git") }
140
142
  end
@@ -267,7 +269,7 @@ module Dependabot
267
269
 
268
270
  scopes = affected_urls.map do |url|
269
271
  url.split(/\%40|@/)[1]&.split(%r{\%2[fF]|/})&.first
270
- end
272
+ end.uniq
271
273
 
272
274
  # Registry used for unscoped packages
273
275
  return if scopes.include?(nil)
@@ -108,8 +108,9 @@ module Dependabot
108
108
  def update_package_json_resolutions(package_json_content:, new_req:,
109
109
  dependency:, old_req:)
110
110
  dep = dependency
111
+ parsed_json_content = JSON.parse(package_json_content)
111
112
  resolutions =
112
- JSON.parse(package_json_content).fetch("resolutions", {}).
113
+ parsed_json_content.fetch("resolutions", parsed_json_content.dig("pnpm", "overrides") || {}).
113
114
  reject { |_, v| v != old_req && v != dep.previous_version }.
114
115
  select { |k, _| k == dep.name || k.end_with?("/#{dep.name}") }
115
116
 
@@ -132,7 +133,7 @@ module Dependabot
132
133
  )
133
134
 
134
135
  content = update_package_json_sections(
135
- ["resolutions"], content, original_line, replacement_line
136
+ %w(resolutions overrides), content, original_line, replacement_line
136
137
  )
137
138
  end
138
139
  content
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dependabot/npm_and_yarn/helpers"
4
+ require "dependabot/npm_and_yarn/update_checker/registry_finder"
5
+ require "dependabot/shared_helpers"
6
+
7
+ module Dependabot
8
+ module NpmAndYarn
9
+ class FileUpdater
10
+ class PnpmLockfileUpdater
11
+ require_relative "package_json_updater"
12
+
13
+ def initialize(dependencies:, dependency_files:, repo_contents_path:, credentials:)
14
+ @dependencies = dependencies
15
+ @dependency_files = dependency_files
16
+ @repo_contents_path = repo_contents_path
17
+ @credentials = credentials
18
+ end
19
+
20
+ def updated_pnpm_lock_content(pnpm_lock)
21
+ @updated_pnpm_lock_content ||= {}
22
+ return @updated_pnpm_lock_content[pnpm_lock.name] if @updated_pnpm_lock_content[pnpm_lock.name]
23
+
24
+ new_content = run_pnpm_update(pnpm_lock: pnpm_lock)
25
+ @updated_pnpm_lock_content[pnpm_lock.name] = new_content
26
+ rescue SharedHelpers::HelperSubprocessFailed => e
27
+ handle_pnpm_lock_updater_error(e, pnpm_lock)
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :dependencies, :dependency_files, :repo_contents_path, :credentials
33
+
34
+ IRRESOLVABLE_PACKAGE = "ERR_PNPM_NO_MATCHING_VERSION"
35
+ INVALID_REQUIREMENT = "ERR_PNPM_SPEC_NOT_SUPPORTED_BY_ANY_RESOLVER"
36
+ MISSING_PACKAGE = /(?<package_req>.*?) is not in the npm registry, or you have no permission to fetch it/
37
+
38
+ def run_pnpm_update(pnpm_lock:)
39
+ SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
40
+ SharedHelpers.with_git_configured(credentials: credentials) do
41
+ run_pnpm_updater
42
+
43
+ write_final_package_json_files
44
+
45
+ run_pnpm_install
46
+
47
+ File.read(pnpm_lock.name)
48
+ end
49
+ end
50
+ end
51
+
52
+ def run_pnpm_updater
53
+ dependency_updates = dependencies.map do |d|
54
+ "#{d.name}@#{d.version}"
55
+ end.join(" ")
56
+
57
+ SharedHelpers.run_shell_command(
58
+ "pnpm install #{dependency_updates} --lockfile-only --ignore-workspace-root-check",
59
+ fingerprint: "pnpm install <dependency_updates> --lockfile-only --ignore-workspace-root-check"
60
+ )
61
+ end
62
+
63
+ def run_pnpm_install
64
+ SharedHelpers.run_shell_command(
65
+ "pnpm install --lockfile-only"
66
+ )
67
+ end
68
+
69
+ def lockfile_dependencies(lockfile)
70
+ @lockfile_dependencies ||= {}
71
+ @lockfile_dependencies[lockfile.name] ||=
72
+ NpmAndYarn::FileParser.new(
73
+ dependency_files: [lockfile, *package_files],
74
+ source: nil,
75
+ credentials: credentials
76
+ ).parse
77
+ end
78
+
79
+ def handle_pnpm_lock_updater_error(error, pnpm_lock)
80
+ error_message = error.message
81
+
82
+ if error_message.include?(IRRESOLVABLE_PACKAGE) || error_message.include?(INVALID_REQUIREMENT)
83
+ raise_resolvability_error(error_message, pnpm_lock)
84
+ end
85
+
86
+ raise unless error_message.match?(MISSING_PACKAGE)
87
+
88
+ package_name = error_message.match(MISSING_PACKAGE).
89
+ named_captures["package_req"].
90
+ split(/(?<=\w)\@/).first
91
+ raise_missing_package_error(package_name, error_message, pnpm_lock)
92
+ end
93
+
94
+ def raise_resolvability_error(error_message, pnpm_lock)
95
+ dependency_names = dependencies.map(&:name).join(", ")
96
+ msg = "Error whilst updating #{dependency_names} in " \
97
+ "#{pnpm_lock.path}:\n#{error_message}"
98
+ raise Dependabot::DependencyFileNotResolvable, msg
99
+ end
100
+
101
+ def raise_missing_package_error(package_name, _error_message, pnpm_lock)
102
+ missing_dep = lockfile_dependencies(pnpm_lock).
103
+ find { |dep| dep.name == package_name }
104
+
105
+ reg = NpmAndYarn::UpdateChecker::RegistryFinder.new(
106
+ dependency: missing_dep,
107
+ credentials: credentials,
108
+ npmrc_file: npmrc_file
109
+ ).registry
110
+
111
+ raise PrivateSourceAuthenticationFailure, reg
112
+ end
113
+
114
+ def write_final_package_json_files
115
+ package_files.each do |file|
116
+ path = file.name
117
+ FileUtils.mkdir_p(Pathname.new(path).dirname)
118
+ File.write(path, updated_package_json_content(file))
119
+ end
120
+ end
121
+
122
+ def updated_package_json_content(file)
123
+ @updated_package_json_content ||= {}
124
+ @updated_package_json_content[file.name] ||=
125
+ PackageJsonUpdater.new(
126
+ package_json: file,
127
+ dependencies: dependencies
128
+ ).updated_package_json.content
129
+ end
130
+
131
+ def package_files
132
+ @package_files ||= dependency_files.select { |f| f.name.end_with?("package.json") }
133
+ end
134
+
135
+ def base_dir
136
+ dependency_files.first.directory
137
+ end
138
+
139
+ def npmrc_file
140
+ dependency_files.find { |f| f.name == ".npmrc" }
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
@@ -342,7 +342,7 @@ module Dependabot
342
342
  if Helpers.yarn_berry?(yarn_lock)
343
343
  File.write(".yarnrc.yml", yarnrc_yml_content) if yarnrc_yml_file
344
344
  else
345
- File.write(".npmrc", npmrc_content) unless Helpers.yarn_berry?(yarn_lock)
345
+ File.write(".npmrc", npmrc_content)
346
346
  File.write(".yarnrc", yarnrc_content) if yarnrc_specifies_private_reg?
347
347
  end
348
348