dependabot-npm_and_yarn 0.133.0 → 0.133.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 389ca43068e415bb536606c04350940eeebf285929a7a770edc9ca7a72b0c6ca
4
- data.tar.gz: d9fd8268110e8eb0c25f368a54effb141e09bab5bebe8e7ec6c80b341d0af8b1
3
+ metadata.gz: 92e7aae218d62d0f572d1707a8a77cab9d5b23808300ffe4e2f85adf71729667
4
+ data.tar.gz: 0cf412aa358d3438218f00cfe0a06acd1fc18139a49634b2b91626833c18e99f
5
5
  SHA512:
6
- metadata.gz: b54f5aaf854e4b7fb7148d6778f4c782baee6ad18b4e9238777e70a052cfdad950722b0bf83fb9b3dc918f26d16437f5c8ba2ffab910bb4369307cacc7ee859a
7
- data.tar.gz: 315f19cb352c7be0508a9a75616e5c6cbe503a2c0ffb2632a7f87337864377b17bc65b7c33f64591434b9ec2d35b99f8f432342e95eab3c5e884a8edfdc7b34b
6
+ metadata.gz: 0f70b817f202208c29aa894f942551144a78a0659ffb5e9f89797973b010da11b0e86f901e7b5fe0bedabe651fd2b2febb0af5a0d5c4bac71e5961bb74565c95
7
+ data.tar.gz: 47b091546f71e3b7b6429faf57c6b5147db9ed71cd4b99a89edaed68b4c95fe81b74eb505de34be12e59751cc911e442c73a7e52d6c227e5a81313bd7d0e9d21
data/helpers/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "@dependabot/yarn-lib": "^1.21.1",
13
- "@npmcli/arborist": "^2.2.0",
13
+ "@npmcli/arborist": "^2.2.1",
14
14
  "detect-indent": "^6.0.0",
15
15
  "npm6": "npm:npm@6.14.11",
16
16
  "npm7": "npm:npm@7.4.0",
data/helpers/yarn.lock CHANGED
@@ -543,18 +543,18 @@
543
543
  "@types/yargs" "^15.0.0"
544
544
  chalk "^4.0.0"
545
545
 
546
- "@npmcli/arborist@^2.0.0", "@npmcli/arborist@^2.0.3", "@npmcli/arborist@^2.2.0":
547
- version "2.2.0"
548
- resolved "https://registry.yarnpkg.com/@npmcli/arborist/-/arborist-2.2.0.tgz#4cd64abd0d6993382631c4064a8bef2c6c680232"
549
- integrity sha512-bnQccUyKUz6Id7GgMnQiTA4E4U6LK5FolkWtVahk29JXiJYXWrRDItnjvcBbzjGAG9mAEK3LxsO3oWDvGVjw0A==
546
+ "@npmcli/arborist@^2.0.0", "@npmcli/arborist@^2.0.3", "@npmcli/arborist@^2.2.1":
547
+ version "2.2.1"
548
+ resolved "https://registry.yarnpkg.com/@npmcli/arborist/-/arborist-2.2.1.tgz#5fbf3b697f3315be124e3477c17793ad9a68828a"
549
+ integrity sha512-J76nY+TYhxNLFAnWy1HqfjszC6dHy5zxHHFt1LJ2pgBDcb00ipNAbTX0qtyv6FPTF67hPErmPKePaKtFr5KvEA==
550
550
  dependencies:
551
- "@npmcli/installed-package-contents" "^1.0.5"
552
- "@npmcli/map-workspaces" "^1.0.1"
551
+ "@npmcli/installed-package-contents" "^1.0.6"
552
+ "@npmcli/map-workspaces" "^1.0.2"
553
553
  "@npmcli/metavuln-calculator" "^1.0.1"
554
554
  "@npmcli/move-file" "^1.1.0"
555
555
  "@npmcli/name-from-folder" "^1.0.1"
556
556
  "@npmcli/node-gyp" "^1.0.1"
557
- "@npmcli/run-script" "^1.8.1"
557
+ "@npmcli/run-script" "^1.8.2"
558
558
  bin-links "^2.2.1"
559
559
  cacache "^15.0.3"
560
560
  common-ancestor-path "^1.0.1"
@@ -565,11 +565,11 @@
565
565
  npm-package-arg "^8.1.0"
566
566
  npm-pick-manifest "^6.1.0"
567
567
  npm-registry-fetch "^9.0.0"
568
- pacote "^11.2.5"
568
+ pacote "^11.2.6"
569
569
  parse-conflict-json "^1.1.1"
570
570
  promise-all-reject-late "^1.0.0"
571
571
  promise-call-limit "^1.0.1"
572
- read-package-json-fast "^1.2.1"
572
+ read-package-json-fast "^2.0.1"
573
573
  readdir-scoped-modules "^1.1.0"
574
574
  semver "^7.3.4"
575
575
  tar "^6.1.0"
@@ -607,25 +607,25 @@
607
607
  unique-filename "^1.1.1"
608
608
  which "^2.0.2"
609
609
 
610
- "@npmcli/installed-package-contents@^1.0.5":
611
- version "1.0.5"
612
- resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-1.0.5.tgz#cc78565e55d9f14d46acf46a96f70934e516fa3d"
613
- integrity sha512-aKIwguaaqb6ViwSOFytniGvLPb9SMCUm39TgM3SfUo7n0TxUMbwoXfpwyvQ4blm10lzbAwTsvjr7QZ85LvTi4A==
610
+ "@npmcli/installed-package-contents@^1.0.6":
611
+ version "1.0.6"
612
+ resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-1.0.6.tgz#c8e4e18d16b51f70d1bcdeeca816074ca60a8bdd"
613
+ integrity sha512-mpAxU8XZbhyqD1N9/fw1vS5wpKTWREDpUrTPdyQUJOT1PRXIM+DsK3Tpiftvvfi1lmvrRD2HusE2ohgGKvo3UA==
614
614
  dependencies:
615
615
  npm-bundled "^1.1.1"
616
616
  npm-normalize-package-bin "^1.0.1"
617
- read-package-json-fast "^1.1.1"
617
+ read-package-json-fast "^2.0.1"
618
618
  readdir-scoped-modules "^1.1.0"
619
619
 
620
- "@npmcli/map-workspaces@^1.0.1":
621
- version "1.0.1"
622
- resolved "https://registry.yarnpkg.com/@npmcli/map-workspaces/-/map-workspaces-1.0.1.tgz#fbbb7d7dfa650f04b8842df94bbdae26041b60fa"
623
- integrity sha512-w6mVyJ2ngWV7O7f9zPjLrQzRDv7leFmNLVnewNuhouV1MYxXz61DXn2ja3AQj6xlnIp9Z/0GdV0/Ut14eVT8Vw==
620
+ "@npmcli/map-workspaces@^1.0.2":
621
+ version "1.0.2"
622
+ resolved "https://registry.yarnpkg.com/@npmcli/map-workspaces/-/map-workspaces-1.0.2.tgz#77f2400470cdc1d61731ba34a85c577ade1616b9"
623
+ integrity sha512-12nBSZ0EI/jRtCCjjQXF+1Twvj+ecxtBXFRomrTXR0xWn8oppc/OM53aPpPG5EnQWrKAAnOS/hEIATm6kxKz/A==
624
624
  dependencies:
625
625
  "@npmcli/name-from-folder" "^1.0.1"
626
626
  glob "^7.1.6"
627
627
  minimatch "^3.0.4"
628
- read-package-json-fast "^1.2.1"
628
+ read-package-json-fast "^2.0.1"
629
629
 
630
630
  "@npmcli/metavuln-calculator@^1.0.1":
631
631
  version "1.0.2"
@@ -649,29 +649,29 @@
649
649
  resolved "https://registry.yarnpkg.com/@npmcli/name-from-folder/-/name-from-folder-1.0.1.tgz#77ecd0a4fcb772ba6fe927e2e2e155fbec2e6b1a"
650
650
  integrity sha512-qq3oEfcLFwNfEYOQ8HLimRGKlD8WSeGEdtUa7hmzpR8Sa7haL1KVQrvgO6wqMjhWFFVjgtrh1gIxDz+P8sjUaA==
651
651
 
652
- "@npmcli/node-gyp@^1.0.0", "@npmcli/node-gyp@^1.0.1":
652
+ "@npmcli/node-gyp@^1.0.1":
653
653
  version "1.0.1"
654
654
  resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-1.0.1.tgz#dedc4ea9b3c6ef207081ebcd82c053ef60edc478"
655
655
  integrity sha512-pBqoKPWmuk9iaEcXlLBVRIA6I1kG9JiICU+sG0NuD6NAR461F+02elHJS4WkQxHW2W5rnsfvP/ClKwmsZ9RaaA==
656
656
 
657
- "@npmcli/promise-spawn@^1.1.0", "@npmcli/promise-spawn@^1.2.0", "@npmcli/promise-spawn@^1.3.0":
657
+ "@npmcli/promise-spawn@^1.1.0", "@npmcli/promise-spawn@^1.2.0", "@npmcli/promise-spawn@^1.3.2":
658
658
  version "1.3.2"
659
659
  resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz#42d4e56a8e9274fba180dabc0aea6e38f29274f5"
660
660
  integrity sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg==
661
661
  dependencies:
662
662
  infer-owner "^1.0.4"
663
663
 
664
- "@npmcli/run-script@^1.2.1", "@npmcli/run-script@^1.3.0", "@npmcli/run-script@^1.8.1":
665
- version "1.8.1"
666
- resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-1.8.1.tgz#729c5ac7293f250b654504d263952703af6da39c"
667
- integrity sha512-G8c86g9cQHyRINosIcpovzv0BkXQc3urhL1ORf3KTe4TS4UBsg2O4Z2feca/W3pfzdHEJzc83ETBW4aKbb3SaA==
664
+ "@npmcli/run-script@^1.2.1", "@npmcli/run-script@^1.3.0", "@npmcli/run-script@^1.8.1", "@npmcli/run-script@^1.8.2":
665
+ version "1.8.2"
666
+ resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-1.8.2.tgz#528627e24da72a94e3156fb78056bca995a828a0"
667
+ integrity sha512-iwKq152Q62zG2rz/zRqT/OLDKcF1nBGTGmFdHRkTV8JRte6bUt18vPG4vOr/uoECecrIuJe1SSyvuUF32yt5BA==
668
668
  dependencies:
669
- "@npmcli/node-gyp" "^1.0.0"
670
- "@npmcli/promise-spawn" "^1.3.0"
669
+ "@npmcli/node-gyp" "^1.0.1"
670
+ "@npmcli/promise-spawn" "^1.3.2"
671
671
  infer-owner "^1.0.4"
672
672
  node-gyp "^7.1.0"
673
673
  puka "^1.0.1"
674
- read-package-json-fast "^1.1.3"
674
+ read-package-json-fast "^2.0.1"
675
675
 
676
676
  "@sinonjs/commons@^1.7.0":
677
677
  version "1.8.2"
@@ -2183,6 +2183,11 @@ err-code@^1.0.0:
2183
2183
  resolved "https://registry.yarnpkg.com/err-code/-/err-code-1.1.2.tgz#06e0116d3028f6aef4806849eb0ea6a748ae6960"
2184
2184
  integrity sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=
2185
2185
 
2186
+ err-code@^2.0.2:
2187
+ version "2.0.3"
2188
+ resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9"
2189
+ integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==
2190
+
2186
2191
  errno@~0.1.7:
2187
2192
  version "0.1.8"
2188
2193
  resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f"
@@ -5820,15 +5825,15 @@ package-json@^4.0.0:
5820
5825
  registry-url "^3.0.3"
5821
5826
  semver "^5.1.0"
5822
5827
 
5823
- pacote@^11.1.11, pacote@^11.1.14, pacote@^11.1.4, pacote@^11.2.5:
5824
- version "11.2.5"
5825
- resolved "https://registry.yarnpkg.com/pacote/-/pacote-11.2.5.tgz#7a1ecc7ac78237b54dcbc99f42ae6cc215d6e64e"
5826
- integrity sha512-KgVY3Rh3xJnhnRCirmsXW8kIdbslrFTnYeTtdzyvObPgj/Tc5VqdmazxsvdXGdIgRB/Km92mBKfuWcGGqgu7UQ==
5828
+ pacote@^11.1.11, pacote@^11.1.14, pacote@^11.1.4, pacote@^11.2.6:
5829
+ version "11.2.6"
5830
+ resolved "https://registry.yarnpkg.com/pacote/-/pacote-11.2.6.tgz#c0426e5d5c8d33aeea3461a75e1390f1ba78f953"
5831
+ integrity sha512-xCl++Hb3aBC7LaWMimbO4xUqZVsEbKDVc6KKDIIyAeBYrmMwY1yJC2nES/lsGd8sdQLUosgBxQyuVNncZ2Ru0w==
5827
5832
  dependencies:
5828
5833
  "@npmcli/git" "^2.0.1"
5829
- "@npmcli/installed-package-contents" "^1.0.5"
5834
+ "@npmcli/installed-package-contents" "^1.0.6"
5830
5835
  "@npmcli/promise-spawn" "^1.2.0"
5831
- "@npmcli/run-script" "^1.3.0"
5836
+ "@npmcli/run-script" "^1.8.2"
5832
5837
  cacache "^15.0.5"
5833
5838
  chownr "^2.0.0"
5834
5839
  fs-minipass "^2.1.0"
@@ -5839,10 +5844,10 @@ pacote@^11.1.11, pacote@^11.1.14, pacote@^11.1.4, pacote@^11.2.5:
5839
5844
  npm-packlist "^2.1.4"
5840
5845
  npm-pick-manifest "^6.0.0"
5841
5846
  npm-registry-fetch "^9.0.0"
5842
- promise-retry "^1.1.1"
5843
- read-package-json-fast "^1.1.3"
5847
+ promise-retry "^2.0.1"
5848
+ read-package-json-fast "^2.0.1"
5844
5849
  rimraf "^3.0.2"
5845
- ssri "^8.0.0"
5850
+ ssri "^8.0.1"
5846
5851
  tar "^6.1.0"
5847
5852
 
5848
5853
  pacote@^9.1.0, pacote@^9.5.12, pacote@^9.5.3:
@@ -6123,6 +6128,14 @@ promise-retry@^1.1.1:
6123
6128
  err-code "^1.0.0"
6124
6129
  retry "^0.10.0"
6125
6130
 
6131
+ promise-retry@^2.0.1:
6132
+ version "2.0.1"
6133
+ resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22"
6134
+ integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==
6135
+ dependencies:
6136
+ err-code "^2.0.2"
6137
+ retry "^0.12.0"
6138
+
6126
6139
  prompts@^2.0.1:
6127
6140
  version "2.4.0"
6128
6141
  resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.0.tgz#4aa5de0723a231d1ee9121c40fdf663df73f61d7"
@@ -6299,7 +6312,7 @@ read-installed@~4.0.3:
6299
6312
  optionalDependencies:
6300
6313
  graceful-fs "^4.1.2"
6301
6314
 
6302
- read-package-json-fast@^1.1.1, read-package-json-fast@^1.1.3, read-package-json-fast@^1.2.1:
6315
+ read-package-json-fast@^1.2.1:
6303
6316
  version "1.2.1"
6304
6317
  resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-1.2.1.tgz#e8518d6f37c99eb3afc26704c5cbb50d7ead82dd"
6305
6318
  integrity sha512-OFbpwnHcv74Oa5YN5WvbOBfLw6yPmPcwvyJJw/tj9cWFBF7juQUDLDSZiOjEcgzfweWeeROOmbPpNN1qm4hcRg==
@@ -6307,6 +6320,14 @@ read-package-json-fast@^1.1.1, read-package-json-fast@^1.1.3, read-package-json-
6307
6320
  json-parse-even-better-errors "^2.3.0"
6308
6321
  npm-normalize-package-bin "^1.0.1"
6309
6322
 
6323
+ read-package-json-fast@^2.0.1:
6324
+ version "2.0.1"
6325
+ resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-2.0.1.tgz#c767f6c634873ffb6bb73788191b65559734f555"
6326
+ integrity sha512-bp6z0tdgLy9KzdfENDIw/53HWAolOVoQTRWXv7PUiqAo3YvvoUVeLr7RWPWq+mu7KUOu9kiT4DvxhUgNUBsvug==
6327
+ dependencies:
6328
+ json-parse-even-better-errors "^2.3.0"
6329
+ npm-normalize-package-bin "^1.0.1"
6330
+
6310
6331
  "read-package-json@1 || 2", read-package-json@^2.0.0, read-package-json@^2.0.13, read-package-json@^2.1.1:
6311
6332
  version "2.1.2"
6312
6333
  resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.1.2.tgz#6992b2b66c7177259feb8eaac73c3acd28b9222a"
@@ -7002,7 +7023,7 @@ ssri@^6.0.0, ssri@^6.0.1:
7002
7023
  dependencies:
7003
7024
  figgy-pudding "^3.5.1"
7004
7025
 
7005
- ssri@^8.0.0:
7026
+ ssri@^8.0.0, ssri@^8.0.1:
7006
7027
  version "8.0.1"
7007
7028
  resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af"
7008
7029
  integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "dependabot/dependency_file"
4
4
  require "dependabot/npm_and_yarn/file_parser"
5
+ require "dependabot/npm_and_yarn/helpers"
5
6
 
6
7
  module Dependabot
7
8
  module NpmAndYarn
@@ -23,23 +24,9 @@ module Dependabot
23
24
  potential_lockfiles_for_manifest(manifest_name).each do |lockfile|
24
25
  details =
25
26
  if [*package_locks, *shrinkwraps].include?(lockfile)
26
- parsed_lockfile = parse_package_lock(lockfile)
27
- parsed_lockfile.dig("dependencies", dependency_name)
27
+ npm_lockfile_details(lockfile, dependency_name, manifest_name)
28
28
  else
29
- parsed_yarn_lock = parse_yarn_lock(lockfile)
30
- details_candidates =
31
- parsed_yarn_lock.
32
- select { |k, _| k.split(/(?<=\w)\@/)[0] == dependency_name }
33
-
34
- # If there's only one entry for this dependency, use it, even if
35
- # the requirement in the lockfile doesn't match
36
- if details_candidates.one?
37
- details_candidates.first.last
38
- else
39
- details_candidates.find do |k, _|
40
- k.split(/(?<=\w)\@/)[1..-1].join("@") == requirement
41
- end&.last
42
- end
29
+ yarn_lockfile_details(lockfile, dependency_name, requirement, manifest_name)
43
30
  end
44
31
 
45
32
  return details if details
@@ -65,6 +52,43 @@ module Dependabot
65
52
  compact
66
53
  end
67
54
 
55
+ def npm_lockfile_details(lockfile, dependency_name, manifest_name)
56
+ parsed_lockfile = parse_package_lock(lockfile)
57
+
58
+ if Helpers.npm_version(lockfile.content) == "npm7"
59
+ parsed_lockfile.dig(
60
+ "packages",
61
+ node_modules_path(manifest_name, dependency_name)
62
+ )&.slice("version", "resolved", "integrity", "dev")
63
+ else
64
+ parsed_lockfile.dig("dependencies", dependency_name)
65
+ end
66
+ end
67
+
68
+ def yarn_lockfile_details(lockfile, dependency_name, requirement, _manifest_name)
69
+ parsed_yarn_lock = parse_yarn_lock(lockfile)
70
+ details_candidates =
71
+ parsed_yarn_lock.
72
+ select { |k, _| k.split(/(?<=\w)\@/)[0] == dependency_name }
73
+
74
+ # If there's only one entry for this dependency, use it, even if
75
+ # the requirement in the lockfile doesn't match
76
+ if details_candidates.one?
77
+ details_candidates.first.last
78
+ else
79
+ details_candidates.find do |k, _|
80
+ k.split(/(?<=\w)\@/)[1..-1].join("@") == requirement
81
+ end&.last
82
+ end
83
+ end
84
+
85
+ def node_modules_path(manifest_name, dependency_name)
86
+ return "node_modules/#{dependency_name}" if manifest_name == "package.json"
87
+
88
+ workspace_path = manifest_name.gsub("/package.json", "")
89
+ File.join(workspace_path, "node_modules", dependency_name)
90
+ end
91
+
68
92
  def yarn_lock_dependencies
69
93
  dependency_set = Dependabot::NpmAndYarn::FileParser::DependencySet.new
70
94
 
@@ -117,11 +117,11 @@ module Dependabot
117
117
  end
118
118
 
119
119
  def package_lock_changed?(package_lock)
120
- package_lock.content != updated_package_lock_content(package_lock)
120
+ package_lock.content != updated_lockfile_content(package_lock)
121
121
  end
122
122
 
123
123
  def shrinkwrap_changed?(shrinkwrap)
124
- shrinkwrap.content != updated_package_lock_content(shrinkwrap)
124
+ shrinkwrap.content != updated_lockfile_content(shrinkwrap)
125
125
  end
126
126
 
127
127
  def updated_manifest_files
@@ -150,7 +150,7 @@ module Dependabot
150
150
 
151
151
  updated_files << updated_file(
152
152
  file: package_lock,
153
- content: updated_package_lock_content(package_lock)
153
+ content: updated_lockfile_content(package_lock)
154
154
  )
155
155
  end
156
156
 
@@ -159,7 +159,7 @@ module Dependabot
159
159
 
160
160
  updated_files << updated_file(
161
161
  file: shrinkwrap,
162
- content: updated_shrinkwrap_content(shrinkwrap)
162
+ content: updated_lockfile_content(shrinkwrap)
163
163
  )
164
164
  end
165
165
 
@@ -181,25 +181,15 @@ module Dependabot
181
181
  )
182
182
  end
183
183
 
184
- def updated_package_lock_content(package_lock)
185
- @updated_package_lock_content ||= {}
186
- @updated_package_lock_content[package_lock.name] ||=
187
- npm_lockfile_updater.updated_lockfile_content(package_lock)
188
- end
189
-
190
- def updated_shrinkwrap_content(shrinkwrap)
191
- @updated_shrinkwrap_content ||= {}
192
- @updated_shrinkwrap_content[shrinkwrap.name] ||=
193
- npm_lockfile_updater.updated_lockfile_content(shrinkwrap)
194
- end
195
-
196
- def npm_lockfile_updater
197
- @npm_lockfile_updater ||=
184
+ def updated_lockfile_content(file)
185
+ @updated_lockfile_content ||= {}
186
+ @updated_lockfile_content[file.name] ||=
198
187
  NpmLockfileUpdater.new(
188
+ lockfile: file,
199
189
  dependencies: dependencies,
200
190
  dependency_files: dependency_files,
201
191
  credentials: credentials
202
- )
192
+ ).updated_lockfile.content
203
193
  end
204
194
 
205
195
  def updated_package_json_content(file)
@@ -17,35 +17,22 @@ module Dependabot
17
17
  require_relative "npmrc_builder"
18
18
  require_relative "package_json_updater"
19
19
 
20
- def initialize(dependencies:, dependency_files:, credentials:)
20
+ def initialize(lockfile:, dependencies:, dependency_files:, credentials:)
21
+ @lockfile = lockfile
21
22
  @dependencies = dependencies
22
23
  @dependency_files = dependency_files
23
24
  @credentials = credentials
24
25
  end
25
26
 
26
- def updated_lockfile_content(lockfile)
27
- return lockfile.content if npmrc_disables_lockfile?
28
- return lockfile.content if updatable_dependencies(lockfile).empty?
29
-
30
- @updated_lockfile_content ||= {}
31
- @updated_lockfile_content[lockfile.name] ||=
32
- SharedHelpers.in_a_temporary_directory do
33
- path = Pathname.new(lockfile.name).dirname.to_s
34
- lockfile_name = Pathname.new(lockfile.name).basename.to_s
35
- write_temporary_dependency_files(lockfile.name)
36
- updated_files = Dir.chdir(path) do
37
- run_current_npm_update(lockfile_name: lockfile_name, lockfile_content: lockfile.content)
38
- end
39
- updated_content = updated_files.fetch(lockfile_name)
40
- post_process_npm_lockfile(lockfile.content, updated_content, lockfile.name)
41
- end
42
- rescue SharedHelpers::HelperSubprocessFailed => e
43
- handle_npm_updater_error(e, lockfile)
27
+ def updated_lockfile
28
+ updated_file = lockfile.dup
29
+ updated_file.content = updated_lockfile_content
30
+ updated_file
44
31
  end
45
32
 
46
33
  private
47
34
 
48
- attr_reader :dependencies, :dependency_files, :credentials
35
+ attr_reader :lockfile, :dependencies, :dependency_files, :credentials
49
36
 
50
37
  UNREACHABLE_GIT = /fatal: repository '(?<url>.*)' not found/.freeze
51
38
  FORBIDDEN_GIT = /fatal: Authentication failed for '(?<url>.*)'/.freeze
@@ -63,6 +50,21 @@ module Dependabot
63
50
  NPM7_MISSING_GIT_REF = /already exists and is not an empty directory/.freeze
64
51
  NPM6_MISSING_GIT_REF = /did not match any file\(s\) known to git/.freeze
65
52
 
53
+ def updated_lockfile_content
54
+ return lockfile.content if npmrc_disables_lockfile?
55
+ return lockfile.content unless updatable_dependencies.any?
56
+
57
+ @updated_lockfile_content ||=
58
+ SharedHelpers.in_a_temporary_directory do
59
+ write_temporary_dependency_files
60
+ updated_files = Dir.chdir(lockfile_directory) { run_current_npm_update }
61
+ updated_lockfile_content = updated_files.fetch(lockfile_basename)
62
+ post_process_npm_lockfile(updated_lockfile_content)
63
+ end
64
+ rescue SharedHelpers::HelperSubprocessFailed => e
65
+ handle_npm_updater_error(e)
66
+ end
67
+
66
68
  def top_level_dependencies
67
69
  dependencies.select(&:top_level?)
68
70
  end
@@ -71,16 +73,14 @@ module Dependabot
71
73
  dependencies.reject(&:top_level?)
72
74
  end
73
75
 
74
- def updatable_dependencies(lockfile)
76
+ def updatable_dependencies
75
77
  dependencies.reject do |dependency|
76
- dependency_up_to_date?(lockfile, dependency) ||
77
- top_level_dependency_update_not_required?(dependency, lockfile)
78
+ dependency_up_to_date?(dependency) || top_level_dependency_update_not_required?(dependency)
78
79
  end
79
80
  end
80
81
 
81
- def lockfile_dependencies(lockfile)
82
- @lockfile_dependencies ||= {}
83
- @lockfile_dependencies[lockfile.name] ||=
82
+ def lockfile_dependencies
83
+ @lockfile_dependencies ||=
84
84
  NpmAndYarn::FileParser.new(
85
85
  dependency_files: [lockfile, *package_files],
86
86
  source: nil,
@@ -88,9 +88,8 @@ module Dependabot
88
88
  ).parse
89
89
  end
90
90
 
91
- def dependency_up_to_date?(lockfile, dependency)
92
- existing_dep = lockfile_dependencies(lockfile).
93
- find { |dep| dep.name == dependency.name }
91
+ def dependency_up_to_date?(dependency)
92
+ existing_dep = lockfile_dependencies.find { |dep| dep.name == dependency.name }
94
93
 
95
94
  # If the dependency is missing but top level it should be treated as
96
95
  # not up to date
@@ -106,93 +105,81 @@ module Dependabot
106
105
  # proj). npm 7 introduces workspace support so we explitly want to
107
106
  # update the root lockfile and check if the dependency is in the
108
107
  # lockfile
109
- def top_level_dependency_update_not_required?(dependency, lockfile)
110
- lockfile_dir = Pathname.new(lockfile.name).dirname.to_s
111
-
112
- requirements_for_path = dependency.requirements.select do |req|
113
- req_dir = Pathname.new(req[:file]).dirname.to_s
114
- req_dir == lockfile_dir
115
- end
116
-
117
- dependency_in_lockfile = lockfile_dependencies(lockfile).any? do |dep|
118
- dep.name == dependency.name
119
- end
120
-
121
- dependency.top_level? && requirements_for_path.empty? && !dependency_in_lockfile
108
+ def top_level_dependency_update_not_required?(dependency)
109
+ dependency.top_level? &&
110
+ !dependency_in_package_json?(dependency) &&
111
+ !dependency_in_lockfile?(dependency)
122
112
  end
123
113
 
124
- def run_current_npm_update(lockfile_name:, lockfile_content:)
125
- top_level_dependency_updates = top_level_dependencies.map do |d|
126
- { name: d.name, version: d.version, requirements: d.requirements }
127
- end
128
-
129
- run_npm_updater(
130
- lockfile_name: lockfile_name,
131
- top_level_dependency_updates: top_level_dependency_updates,
132
- lockfile_content: lockfile_content
133
- )
114
+ def run_current_npm_update
115
+ run_npm_updater(top_level_dependencies: top_level_dependencies)
134
116
  end
135
117
 
136
- def run_previous_npm_update(lockfile_name:, lockfile_content:)
118
+ def run_previous_npm_update
137
119
  previous_top_level_dependencies = top_level_dependencies.map do |d|
138
- {
120
+ Dependabot::Dependency.new(
139
121
  name: d.name,
122
+ package_manager: d.package_manager,
140
123
  version: d.previous_version,
141
- requirements: d.previous_requirements
142
- }
124
+ previous_version: d.previous_version,
125
+ requirements: d.previous_requirements,
126
+ previous_requirements: d.previous_requirements
127
+ )
143
128
  end
144
129
 
145
- run_npm_updater(
146
- lockfile_name: lockfile_name,
147
- top_level_dependency_updates: previous_top_level_dependencies,
148
- lockfile_content: lockfile_content
149
- )
130
+ run_npm_updater(top_level_dependencies: previous_top_level_dependencies)
150
131
  end
151
132
 
152
- def run_npm_updater(lockfile_name:, top_level_dependency_updates:, lockfile_content:)
133
+ def run_npm_updater(top_level_dependencies:)
153
134
  SharedHelpers.with_git_configured(credentials: credentials) do
154
- if top_level_dependency_updates.any?
155
- run_npm_top_level_updater(
156
- lockfile_name: lockfile_name,
157
- top_level_dependency_updates: top_level_dependency_updates,
158
- lockfile_content: lockfile_content
159
- )
135
+ if top_level_dependencies.any?
136
+ run_npm_top_level_updater(top_level_dependencies: top_level_dependencies)
160
137
  else
161
- run_npm_subdependency_updater(lockfile_name: lockfile_name, lockfile_content: lockfile_content)
138
+ run_npm_subdependency_updater
162
139
  end
163
140
  end
164
141
  end
165
142
 
166
- def run_npm_top_level_updater(lockfile_name:, top_level_dependency_updates:, lockfile_content:)
167
- if npm7?(lockfile_content)
168
- run_npm_7_top_level_updater(
169
- lockfile_name: lockfile_name,
170
- top_level_dependency_updates: top_level_dependency_updates
171
- )
143
+ def run_npm_top_level_updater(top_level_dependencies:)
144
+ if npm7?
145
+ run_npm_7_top_level_updater(top_level_dependencies: top_level_dependencies)
172
146
  else
173
147
  SharedHelpers.run_helper_subprocess(
174
148
  command: NativeHelpers.helper_path,
175
149
  function: "npm6:update",
176
150
  args: [
177
151
  Dir.pwd,
178
- lockfile_name,
179
- top_level_dependency_updates
152
+ lockfile_basename,
153
+ top_level_dependencies.map(&:to_h)
180
154
  ]
181
155
  )
182
156
  end
183
157
  end
184
158
 
185
- def run_npm_7_top_level_updater(lockfile_name:, top_level_dependency_updates:)
186
- # - `--dry-run=false` the updater sets a global .npmrc with dry-run: true to
187
- # work around an issue in npm 6, we don't want that here
159
+ def run_npm_7_top_level_updater(top_level_dependencies:)
160
+ dependencies_in_current_package_json = top_level_dependencies.any? do |dependency|
161
+ dependency_in_package_json?(dependency)
162
+ end
163
+
164
+ # NOTE: When updating a dependency in a nested workspace project we
165
+ # need to run `npm install` without any arguments to update the root
166
+ # level lockfile after having updated the nested packages package.json
167
+ # requirement, otherwise npm will add the dependency as a new
168
+ # top-level dependency to the root lockfile.
169
+ install_args = ""
170
+ if dependencies_in_current_package_json
171
+ # TODO: Update the npm 6 updater to use these args as we currently
172
+ # do the same in the js updater helper, we've kept it seperate for
173
+ # the npm 7 rollout
174
+ install_args = top_level_dependencies.map { |dependency| npm_install_args(dependency) }
175
+ end
176
+
177
+ # NOTE: npm options
188
178
  # - `--force` ignores checks for platform (os, cpu) and engines
189
- # - `--ignore-scripts` disables prepare and prepack scripts which are run
190
- # when installing git dependencies
191
- flattenend_manifest_dependencies = flattenend_manifest_dependencies_for_lockfile_name(lockfile_name)
192
- install_args = npm_top_level_updater_args(
193
- top_level_dependency_updates: top_level_dependency_updates,
194
- flattenend_manifest_dependencies: flattenend_manifest_dependencies
195
- )
179
+ # - `--dry-run=false` the updater sets a global .npmrc with dry-run:
180
+ # true to work around an issue in npm 6, we don't want that here
181
+ # - `--ignore-scripts` disables prepare and prepack scripts which are
182
+ # run when installing git dependencies
196
183
  command = [
197
184
  "npm",
198
185
  "install",
@@ -204,26 +191,27 @@ module Dependabot
204
191
  "--package-lock-only"
205
192
  ].join(" ")
206
193
  SharedHelpers.run_shell_command(command)
207
- { lockfile_name => File.read(lockfile_name) }
194
+ { lockfile_basename => File.read(lockfile_basename) }
208
195
  end
209
196
 
210
- def run_npm_subdependency_updater(lockfile_name:, lockfile_content:)
211
- if npm7?(lockfile_content)
212
- run_npm_7_subdependency_updater(lockfile_name: lockfile_name)
197
+ def run_npm_subdependency_updater
198
+ if npm7?
199
+ run_npm_7_subdependency_updater
213
200
  else
214
201
  SharedHelpers.run_helper_subprocess(
215
202
  command: NativeHelpers.helper_path,
216
203
  function: "npm6:updateSubdependency",
217
- args: [Dir.pwd, lockfile_name, sub_dependencies.map(&:to_h)]
204
+ args: [Dir.pwd, lockfile_basename, sub_dependencies.map(&:to_h)]
218
205
  )
219
206
  end
220
207
  end
221
208
 
222
- def run_npm_7_subdependency_updater(lockfile_name:)
209
+ def run_npm_7_subdependency_updater
223
210
  dependency_names = sub_dependencies.map(&:name)
211
+ # NOTE: npm options
212
+ # - `--force` ignores checks for platform (os, cpu) and engines
224
213
  # - `--dry-run=false` the updater sets a global .npmrc with dry-run: true to
225
214
  # work around an issue in npm 6, we don't want that here
226
- # - `--force` ignores checks for platform (os, cpu) and engines
227
215
  # - `--ignore-scripts` disables prepare and prepack scripts which are run
228
216
  # when installing git dependencies
229
217
  command = [
@@ -237,59 +225,64 @@ module Dependabot
237
225
  "--package-lock-only"
238
226
  ].join(" ")
239
227
  SharedHelpers.run_shell_command(command)
240
- { lockfile_name => File.read(lockfile_name) }
228
+ { lockfile_basename => File.read(lockfile_basename) }
241
229
  end
242
230
 
243
- # TODO: Update the npm 6 updater to use these args as we currently do
244
- # the same in the js updater helper, we've kept it seperate for the npm
245
- # 7 rollout
246
- def npm_top_level_updater_args(top_level_dependency_updates:, flattenend_manifest_dependencies:)
247
- top_level_dependency_updates.map do |dependency|
248
- # NOTE: For git dependencies we loose some information about the
249
- # requirement that's only available in the package.json, e.g. when
250
- # specifying a semver tag:
251
- # `dependabot/depeendabot-core#semver:^0.1` - this is required to
252
- # pass the correct install argument to `npm install`
253
- existing_version_requirement = flattenend_manifest_dependencies[dependency.fetch(:name)]
254
- npm_install_args(
255
- dependency.fetch(:name),
256
- dependency.fetch(:version),
257
- dependency.fetch(:requirements),
258
- existing_version_requirement
259
- )
260
- end
231
+ def updated_version_requirement_for_dependency(dependency)
232
+ flattenend_manifest_dependencies[dependency.name]
261
233
  end
262
234
 
263
- def flattenend_manifest_dependencies_for_lockfile_name(lockfile_name)
264
- package_json_content = updated_package_json_content_for_lockfile_name(lockfile_name)
265
- return {} unless package_json_content
235
+ # TODO: Add the raw updated requirement to the Dependency instance
236
+ # instead of fishing it out of the updated package json, we need to do
237
+ # this because we don't store the same requirement in
238
+ # Dependency#requirements for git dependencies - see PackageJsonUpdater
239
+ def flattenend_manifest_dependencies
240
+ return @flattenend_manifest_dependencies if defined?(@flattenend_manifest_dependencies)
266
241
 
267
- parsed_package = JSON.parse(package_json_content)
268
- NpmAndYarn::FileParser::DEPENDENCY_TYPES.inject({}) do |deps, type|
269
- deps.merge(parsed_package[type] || {})
270
- end
242
+ @flattenend_manifest_dependencies =
243
+ NpmAndYarn::FileParser::DEPENDENCY_TYPES.inject({}) do |deps, type|
244
+ deps.merge(parsed_package_json[type] || {})
245
+ end
271
246
  end
272
247
 
273
- def npm_install_args(dep_name, desired_version, requirements, existing_version_requirement)
274
- git_requirement = requirements.find { |req| req[:source] && req[:source][:type] == "git" }
248
+ def npm_install_args(dependency)
249
+ git_requirement = dependency.requirements.find { |req| req[:source] && req[:source][:type] == "git" }
275
250
 
276
251
  if git_requirement
277
- existing_version_requirement ||= git_requirement[:source][:url]
252
+ # NOTE: For git dependencies we loose some information about the
253
+ # requirement that's only available in the package.json, e.g. when
254
+ # specifying a semver tag:
255
+ # `dependabot/depeendabot-core#semver:^0.1` - this is required to
256
+ # pass the correct install argument to `npm install`
257
+ updated_version_requirement = updated_version_requirement_for_dependency(dependency)
258
+ updated_version_requirement ||= git_requirement[:source][:url]
278
259
 
279
260
  # NOTE: Git is configured to auth over https while updating
280
- existing_version_requirement = existing_version_requirement.gsub(
261
+ updated_version_requirement = updated_version_requirement.gsub(
281
262
  %r{git\+ssh://git@(.*?)[:/]}, 'https://\1/'
282
263
  )
283
264
 
284
265
  # NOTE: Keep any semver range that has already been updated by the
285
266
  # PackageJsonUpdater when installing the new version
286
- if existing_version_requirement.include?(desired_version)
287
- "#{dep_name}@#{existing_version_requirement}"
267
+ if updated_version_requirement.include?(dependency.version)
268
+ "#{dependency.name}@#{updated_version_requirement}"
288
269
  else
289
- "#{dep_name}@#{existing_version_requirement.sub(/#.*/, '')}##{desired_version}"
270
+ "#{dependency.name}@#{updated_version_requirement.sub(/#.*/, '')}##{dependency.version}"
290
271
  end
291
272
  else
292
- "#{dep_name}@#{desired_version}"
273
+ "#{dependency.name}@#{dependency.version}"
274
+ end
275
+ end
276
+
277
+ def dependency_in_package_json?(dependency)
278
+ dependency.requirements.any? do |req|
279
+ req[:file] == package_json.name
280
+ end
281
+ end
282
+
283
+ def dependency_in_lockfile?(dependency)
284
+ lockfile_dependencies.any? do |dep|
285
+ dep.name == dependency.name
293
286
  end
294
287
  end
295
288
 
@@ -297,14 +290,14 @@ module Dependabot
297
290
  # rubocop:disable Metrics/CyclomaticComplexity
298
291
  # rubocop:disable Metrics/PerceivedComplexity
299
292
  # rubocop:disable Metrics/MethodLength
300
- def handle_npm_updater_error(error, lockfile)
293
+ def handle_npm_updater_error(error)
301
294
  error_message = error.message
302
295
  if error_message.match?(MISSING_PACKAGE)
303
296
  package_name = error_message.match(MISSING_PACKAGE).
304
297
  named_captures["package_req"]
305
298
  sanitized_name = sanitize_package_name(package_name)
306
299
  sanitized_error = error_message.gsub(package_name, sanitized_name)
307
- handle_missing_package(sanitized_name, sanitized_error, lockfile)
300
+ handle_missing_package(sanitized_name, sanitized_error)
308
301
  end
309
302
 
310
303
  # Invalid package: When the package.json doesn't include a name or
@@ -316,7 +309,7 @@ module Dependabot
316
309
  if error_message.match?(INVALID_PACKAGE) ||
317
310
  error_message.include?("Invalid package name") ||
318
311
  error_message.include?(sub_dep_local_path_error)
319
- raise_resolvability_error(error_message, lockfile)
312
+ raise_resolvability_error(error_message)
320
313
  end
321
314
 
322
315
  # TODO: Move this logic to the version resolver and check if a new
@@ -340,7 +333,7 @@ module Dependabot
340
333
  # queries
341
334
  if error_message.include?("No matching vers") &&
342
335
  dependencies_in_error_message?(error_message) &&
343
- resolvable_before_update?(lockfile)
336
+ resolvable_before_update?
344
337
 
345
338
  # Raise a bespoke error so we can capture and ignore it if
346
339
  # we're trying to create a new PR (which will be created
@@ -353,7 +346,7 @@ module Dependabot
353
346
  named_captures["package_req"]
354
347
  sanitized_name = sanitize_package_name(package_name)
355
348
  sanitized_error = error_message.gsub(package_name, sanitized_name)
356
- handle_missing_package(sanitized_name, sanitized_error, lockfile)
349
+ handle_missing_package(sanitized_name, sanitized_error)
357
350
  end
358
351
 
359
352
  # Some private registries return a 403 when the user is readonly
@@ -362,7 +355,7 @@ module Dependabot
362
355
  named_captures["package_req"]
363
356
  sanitized_name = sanitize_package_name(package_name)
364
357
  sanitized_error = error_message.gsub(package_name, sanitized_name)
365
- handle_missing_package(sanitized_name, sanitized_error, lockfile)
358
+ handle_missing_package(sanitized_name, sanitized_error)
366
359
  end
367
360
 
368
361
  if (git_error = error_message.match(UNREACHABLE_GIT) || error_message.match(FORBIDDEN_GIT))
@@ -379,9 +372,8 @@ module Dependabot
379
372
  # people to re-generate their lockfiles (Future feature idea: add a
380
373
  # way to click-to-fix the lockfile from the issue)
381
374
  if error_message.include?("Cannot read property 'match' of ") &&
382
- !resolvable_before_update?(lockfile)
383
- raise_missing_lockfile_version_resolvability_error(error_message,
384
- lockfile)
375
+ !resolvable_before_update?
376
+ raise_missing_lockfile_version_resolvability_error(error_message)
385
377
  end
386
378
 
387
379
  if (error_message.include?("No matching vers") ||
@@ -390,8 +382,8 @@ module Dependabot
390
382
  error_message.include?("Invalid tag name") ||
391
383
  error_message.match?(NPM6_MISSING_GIT_REF) ||
392
384
  error_message.match?(NPM7_MISSING_GIT_REF)) &&
393
- !resolvable_before_update?(lockfile)
394
- raise_resolvability_error(error_message, lockfile)
385
+ !resolvable_before_update?
386
+ raise_resolvability_error(error_message)
395
387
  end
396
388
 
397
389
  # NOTE: This check was introduced in npm7/arborist
@@ -407,17 +399,15 @@ module Dependabot
407
399
  # rubocop:enable Metrics/PerceivedComplexity
408
400
  # rubocop:enable Metrics/MethodLength
409
401
 
410
- def raise_resolvability_error(error_message, lockfile)
402
+ def raise_resolvability_error(error_message)
411
403
  dependency_names = dependencies.map(&:name).join(", ")
412
404
  msg = "Error whilst updating #{dependency_names} in "\
413
405
  "#{lockfile.path}:\n#{error_message}"
414
406
  raise Dependabot::DependencyFileNotResolvable, msg
415
407
  end
416
408
 
417
- def raise_missing_lockfile_version_resolvability_error(error_message,
418
- lockfile)
419
- lockfile_dir = Pathname.new(lockfile.name).dirname
420
- modules_path = lockfile_dir.join("node_modules")
409
+ def raise_missing_lockfile_version_resolvability_error(error_message)
410
+ modules_path = File.join(lockfile_directory, "node_modules")
421
411
  # NOTE: don't include the dependency names to prevent opening
422
412
  # multiple issues for each dependency that fails because we unique
423
413
  # issues on the error message (issue detail) on the backend
@@ -432,11 +422,10 @@ module Dependabot
432
422
  raise Dependabot::DependencyFileNotResolvable, msg
433
423
  end
434
424
 
435
- def handle_missing_package(package_name, error_message, lockfile)
436
- missing_dep = lockfile_dependencies(lockfile).
437
- find { |dep| dep.name == package_name }
425
+ def handle_missing_package(package_name, error_message)
426
+ missing_dep = lockfile_dependencies.find { |dep| dep.name == package_name }
438
427
 
439
- raise_resolvability_error(error_message, lockfile) unless missing_dep
428
+ raise_resolvability_error(error_message) unless missing_dep
440
429
 
441
430
  reg = NpmAndYarn::UpdateChecker::RegistryFinder.new(
442
431
  dependency: missing_dep,
@@ -458,23 +447,14 @@ module Dependabot
458
447
  end
459
448
  end
460
449
 
461
- def resolvable_before_update?(lockfile)
462
- @resolvable_before_update ||= {}
463
- return @resolvable_before_update[lockfile.name] if @resolvable_before_update.key?(lockfile.name)
450
+ def resolvable_before_update?
451
+ return @resolvable_before_update if defined?(@resolvable_before_update)
464
452
 
465
- @resolvable_before_update[lockfile.name] =
453
+ @resolvable_before_update =
466
454
  begin
467
455
  SharedHelpers.in_a_temporary_directory do
468
- write_temporary_dependency_files(
469
- lockfile.name,
470
- update_package_json: false
471
- )
472
-
473
- lockfile_name = Pathname.new(lockfile.name).basename.to_s
474
- path = Pathname.new(lockfile.name).dirname.to_s
475
- Dir.chdir(path) do
476
- run_previous_npm_update(lockfile_name: lockfile_name, lockfile_content: lockfile.content)
477
- end
456
+ write_temporary_dependency_files(update_package_json: false)
457
+ Dir.chdir(lockfile_directory) { run_previous_npm_update }
478
458
  end
479
459
 
480
460
  true
@@ -492,12 +472,10 @@ module Dependabot
492
472
  end
493
473
  end
494
474
 
495
- def write_temporary_dependency_files(lockfile_name,
496
- update_package_json: true)
497
- write_lockfiles(lockfile_name)
475
+ def write_temporary_dependency_files(update_package_json: true)
476
+ write_lockfiles
498
477
 
499
- dir = Pathname.new(lockfile_name).dirname
500
- File.write(File.join(dir, ".npmrc"), npmrc_content)
478
+ File.write(File.join(lockfile_directory, ".npmrc"), npmrc_content)
501
479
 
502
480
  package_files.each do |file|
503
481
  path = file.name
@@ -524,9 +502,9 @@ module Dependabot
524
502
  end
525
503
  end
526
504
 
527
- def write_lockfiles(lockfile_name)
505
+ def write_lockfiles
528
506
  excluded_lock =
529
- case lockfile_name
507
+ case lockfile.name
530
508
  when "package-lock.json" then "npm-shrinkwrap.json"
531
509
  when "npm-shrinkwrap.json" then "package-lock.json"
532
510
  end
@@ -627,22 +605,86 @@ module Dependabot
627
605
  @git_ssh_requirements_to_swap
628
606
  end
629
607
 
630
- def post_process_npm_lockfile(original_content, updated_content, lockfile_name)
631
- updated_content = replace_project_metadata(updated_content, original_content)
632
-
608
+ def post_process_npm_lockfile(updated_lockfile_content)
633
609
  # Switch SSH requirements back for git dependencies
634
- updated_content = replace_swapped_git_ssh_requirements(updated_content)
610
+ updated_lockfile_content = replace_swapped_git_ssh_requirements(updated_lockfile_content)
635
611
 
636
612
  # Switch from details back for git dependencies (they will have
637
613
  # changed because we locked them)
638
- updated_content = replace_locked_git_dependencies(updated_content)
614
+ updated_lockfile_content = replace_locked_git_dependencies(updated_lockfile_content)
615
+
616
+ parsed_updated_lockfile_content = JSON.parse(updated_lockfile_content)
617
+
618
+ # Restore lockfile name attribute from the original lockfile
619
+ updated_lockfile_content = replace_project_name(updated_lockfile_content, parsed_updated_lockfile_content)
620
+
621
+ # Restore npm 7 "packages" "name" entry from package.json if previously set
622
+ updated_lockfile_content = restore_packages_name(updated_lockfile_content, parsed_updated_lockfile_content)
639
623
 
640
624
  # Switch back npm 7 lockfile "pacakages" requirements from the package.json
641
- updated_content = restore_locked_package_dependencies(lockfile_name, updated_content)
625
+ updated_lockfile_content = restore_locked_package_dependencies(
626
+ updated_lockfile_content, parsed_updated_lockfile_content
627
+ )
642
628
 
643
629
  # Switch back the protocol of tarball resolutions if they've changed
644
630
  # (fixes an npm bug, which appears to be applied inconsistently)
645
- replace_tarball_urls(updated_content)
631
+ replace_tarball_urls(updated_lockfile_content)
632
+ end
633
+
634
+ def replace_project_name(updated_lockfile_content, parsed_updated_lockfile_content)
635
+ current_name = parsed_updated_lockfile_content["name"]
636
+ original_name = parsed_lockfile["name"]
637
+ if original_name
638
+ updated_lockfile_content = replace_lockfile_name_attribute(
639
+ current_name, original_name, updated_lockfile_content
640
+ )
641
+ end
642
+ updated_lockfile_content
643
+ end
644
+
645
+ def restore_packages_name(updated_lockfile_content, parsed_updated_lockfile_content)
646
+ return updated_lockfile_content unless npm7?
647
+
648
+ current_name = parsed_updated_lockfile_content.dig("packages", "", "name")
649
+ original_name = parsed_lockfile.dig("packages", "", "name")
650
+
651
+ # TODO: Submit a patch to npm fixing this issue making `npm install`
652
+ # consistent with `npm install --package-lock-only`
653
+ #
654
+ # NOTE: This is a workaround for npm adding a `name` attribute to the
655
+ # packages section in the lockfile because we install using
656
+ # `--package-lock-only`
657
+ if !original_name
658
+ updated_lockfile_content = remove_lockfile_packages_name_attribute(
659
+ current_name, updated_lockfile_content
660
+ )
661
+ elsif original_name && original_name != current_name
662
+ updated_lockfile_content = replace_lockfile_packages_name_attribute(
663
+ current_name, original_name, updated_lockfile_content
664
+ )
665
+ end
666
+
667
+ updated_lockfile_content
668
+ end
669
+
670
+ def replace_lockfile_name_attribute(current_name, original_name, updated_lockfile_content)
671
+ updated_lockfile_content.sub(
672
+ /"name":\s"#{current_name}"/,
673
+ "\"name\": \"#{original_name}\""
674
+ )
675
+ end
676
+
677
+ def replace_lockfile_packages_name_attribute(current_name, original_name, updated_lockfile_content)
678
+ packages_key_line = '"": {'
679
+ updated_lockfile_content.sub(
680
+ /(#{packages_key_line}[\n\s]+"name":\s)"#{current_name}"/,
681
+ '\1"' + original_name + '"'
682
+ )
683
+ end
684
+
685
+ def remove_lockfile_packages_name_attribute(current_name, updated_lockfile_content)
686
+ packages_key_line = '"": {'
687
+ updated_lockfile_content.gsub(/(#{packages_key_line})[\n\s]+"name":\s"#{current_name}",/, '\1')
646
688
  end
647
689
 
648
690
  # NOTE: This is a workaround to "sync" what's in package.json
@@ -654,43 +696,38 @@ module Dependabot
654
696
  # `package.json` requirement for eslint at `^1.0.0`, in which case we
655
697
  # need to copy this from the manifest to the lockfile after the update
656
698
  # has finished.
657
- def restore_locked_package_dependencies(lockfile_name, lockfile_content)
658
- return lockfile_content unless npm7?(lockfile_content)
659
-
660
- original_package = updated_package_json_content_for_lockfile_name(lockfile_name)
661
- return lockfile_content unless original_package
699
+ def restore_locked_package_dependencies(updated_lockfile_content, parsed_updated_lockfile_content)
700
+ return updated_lockfile_content unless npm7?
662
701
 
663
- parsed_package = JSON.parse(original_package)
664
- parsed_lockfile = JSON.parse(lockfile_content)
665
702
  dependency_names_to_restore = (dependencies.map(&:name) + git_dependencies_to_lock.keys).uniq
666
703
 
667
704
  NpmAndYarn::FileParser::DEPENDENCY_TYPES.each do |type|
668
- parsed_package.fetch(type, {}).each do |dependency_name, original_requirement|
705
+ parsed_package_json.fetch(type, {}).each do |dependency_name, original_requirement|
669
706
  next unless dependency_names_to_restore.include?(dependency_name)
670
707
 
671
- locked_requirement = parsed_lockfile.dig("packages", "", type, dependency_name)
708
+ locked_requirement = parsed_updated_lockfile_content.dig("packages", "", type, dependency_name)
672
709
  next unless locked_requirement
673
710
 
674
711
  locked_req = %("#{dependency_name}": "#{locked_requirement}")
675
712
  original_req = %("#{dependency_name}": "#{original_requirement}")
676
- lockfile_content = lockfile_content.gsub(locked_req, original_req)
713
+ updated_lockfile_content = updated_lockfile_content.gsub(locked_req, original_req)
677
714
  end
678
715
  end
679
716
 
680
- lockfile_content
717
+ updated_lockfile_content
681
718
  end
682
719
 
683
- def replace_swapped_git_ssh_requirements(lockfile_content)
720
+ def replace_swapped_git_ssh_requirements(updated_lockfile_content)
684
721
  git_ssh_requirements_to_swap.each do |req|
685
722
  new_r = req.gsub(%r{git\+ssh://git@(.*?)[:/]}, 'git+https://\1/')
686
723
  old_r = req.gsub(%r{git@(.*?)[:/]}, 'git@\1/')
687
- lockfile_content = lockfile_content.gsub(new_r, old_r)
724
+ updated_lockfile_content = updated_lockfile_content.gsub(new_r, old_r)
688
725
  end
689
726
 
690
- lockfile_content
727
+ updated_lockfile_content
691
728
  end
692
729
 
693
- def replace_locked_git_dependencies(lockfile_content)
730
+ def replace_locked_git_dependencies(updated_lockfile_content)
694
731
  # Switch from details back for git dependencies (they will have
695
732
  # changed because we locked them)
696
733
  git_dependencies_to_lock.each do |dependency_name, details|
@@ -701,44 +738,33 @@ module Dependabot
701
738
  # updates the lockfile "from" field to the new git commit when we
702
739
  # run npm install
703
740
  original_from = %("from": "#{details[:from]}")
704
- if npm7?(lockfile_content)
741
+ if npm7?
705
742
  # NOTE: The `from` syntax has changed in npm 7 to inclued the dependency name
706
743
  npm7_locked_from = %("from": "#{dependency_name}@#{details[:version]}")
707
- lockfile_content = lockfile_content.gsub(npm7_locked_from, original_from)
744
+ updated_lockfile_content = updated_lockfile_content.gsub(npm7_locked_from, original_from)
708
745
  else
709
746
  npm6_locked_from = %("from": "#{details[:version]}")
710
- lockfile_content = lockfile_content.gsub(npm6_locked_from, original_from)
747
+ updated_lockfile_content = updated_lockfile_content.gsub(npm6_locked_from, original_from)
711
748
  end
712
749
  end
713
750
 
714
- lockfile_content
751
+ updated_lockfile_content
715
752
  end
716
753
 
717
- def replace_tarball_urls(lockfile_content)
754
+ def replace_tarball_urls(updated_lockfile_content)
718
755
  tarball_urls.each do |url|
719
756
  trimmed_url = url.gsub(/(\d+\.)*tgz$/, "")
720
757
  incorrect_url = if url.start_with?("https")
721
758
  trimmed_url.gsub(/^https:/, "http:")
722
759
  else trimmed_url.gsub(/^http:/, "https:")
723
760
  end
724
- lockfile_content = lockfile_content.gsub(
761
+ updated_lockfile_content = updated_lockfile_content.gsub(
725
762
  /#{Regexp.quote(incorrect_url)}(?=(\d+\.)*tgz")/,
726
763
  trimmed_url
727
764
  )
728
765
  end
729
766
 
730
- lockfile_content
731
- end
732
-
733
- def replace_project_metadata(new_content, old_content)
734
- old_name = old_content.match(/(?<="name": ").*(?=",)/)&.to_s
735
-
736
- if old_name
737
- new_content = new_content.
738
- sub(/(?<="name": ").*(?=",)/, old_name)
739
- end
740
-
741
- new_content
767
+ updated_lockfile_content
742
768
  end
743
769
 
744
770
  def tarball_urls
@@ -765,15 +791,6 @@ module Dependabot
765
791
  ).npmrc_content
766
792
  end
767
793
 
768
- def updated_package_json_content_for_lockfile_name(lockfile_name)
769
- lockfile_basename = Pathname.new(lockfile_name).basename.to_s
770
- package_name = lockfile_name.sub(lockfile_basename, "package.json")
771
- package_json = package_files.find { |f| f.name == package_name }
772
- return unless package_json
773
-
774
- updated_package_json_content(package_json)
775
- end
776
-
777
794
  def updated_package_json_content(file)
778
795
  @updated_package_json_content ||= {}
779
796
  @updated_package_json_content[file.name] ||=
@@ -787,8 +804,10 @@ module Dependabot
787
804
  npmrc_content.match?(/^package-lock\s*=\s*false/)
788
805
  end
789
806
 
790
- def npm7?(lockfile_content)
791
- Dependabot::NpmAndYarn::Helpers.npm_version(lockfile_content) == "npm7"
807
+ def npm7?
808
+ return @npm7 if defined?(@npm7)
809
+
810
+ @npm7 = Dependabot::NpmAndYarn::Helpers.npm_version(lockfile.content) == "npm7"
792
811
  end
793
812
 
794
813
  def sanitized_package_json_content(content)
@@ -802,6 +821,30 @@ module Dependabot
802
821
  package_name.gsub("%2f", "/").gsub("%2F", "/")
803
822
  end
804
823
 
824
+ def lockfile_directory
825
+ Pathname.new(lockfile.name).dirname.to_s
826
+ end
827
+
828
+ def lockfile_basename
829
+ Pathname.new(lockfile.name).basename.to_s
830
+ end
831
+
832
+ def parsed_lockfile
833
+ @parsed_lockfile ||= JSON.parse(lockfile.content)
834
+ end
835
+
836
+ def parsed_package_json
837
+ return {} unless package_json
838
+ return @parsed_package_json if defined?(@parsed_package_json)
839
+
840
+ @parsed_package_json = JSON.parse(updated_package_json_content(package_json))
841
+ end
842
+
843
+ def package_json
844
+ package_name = lockfile.name.sub(lockfile_basename, "package.json")
845
+ package_files.find { |f| f.name == package_name }
846
+ end
847
+
805
848
  def package_locks
806
849
  @package_locks ||=
807
850
  dependency_files.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-npm_and_yarn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.133.0
4
+ version: 0.133.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-09 00:00:00.000000000 Z
11
+ date: 2021-02-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dependabot-common
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.133.0
19
+ version: 0.133.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 0.133.0
26
+ version: 0.133.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: byebug
29
29
  requirement: !ruby/object:Gem::Requirement