bibliothecary 11.0.1 → 12.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +2 -1
  3. data/.rubocop.yml +10 -2
  4. data/.ruby-version +1 -1
  5. data/CHANGELOG.md +24 -0
  6. data/Gemfile +16 -1
  7. data/Rakefile +2 -0
  8. data/bibliothecary.gemspec +11 -14
  9. data/bin/bibliothecary +2 -1
  10. data/bin/console +1 -0
  11. data/lib/bibliothecary/analyser/analysis.rb +13 -8
  12. data/lib/bibliothecary/analyser/determinations.rb +2 -0
  13. data/lib/bibliothecary/analyser/matchers.rb +17 -17
  14. data/lib/bibliothecary/analyser.rb +11 -8
  15. data/lib/bibliothecary/cli.rb +3 -1
  16. data/lib/bibliothecary/configuration.rb +3 -11
  17. data/lib/bibliothecary/dependency.rb +17 -15
  18. data/lib/bibliothecary/exceptions.rb +6 -2
  19. data/lib/bibliothecary/file_info.rb +9 -11
  20. data/lib/bibliothecary/multi_parsers/bundler_like_manifest.rb +13 -10
  21. data/lib/bibliothecary/multi_parsers/cyclonedx.rb +10 -8
  22. data/lib/bibliothecary/multi_parsers/dependencies_csv.rb +11 -4
  23. data/lib/bibliothecary/multi_parsers/json_runtime.rb +5 -2
  24. data/lib/bibliothecary/multi_parsers/spdx.rb +24 -19
  25. data/lib/bibliothecary/parsers/bower.rb +5 -3
  26. data/lib/bibliothecary/parsers/cargo.rb +10 -4
  27. data/lib/bibliothecary/parsers/cocoapods.rb +15 -11
  28. data/lib/bibliothecary/parsers/conda.rb +56 -33
  29. data/lib/bibliothecary/parsers/cpan.rb +6 -4
  30. data/lib/bibliothecary/parsers/cran.rb +10 -6
  31. data/lib/bibliothecary/parsers/dub.rb +4 -2
  32. data/lib/bibliothecary/parsers/elm.rb +4 -1
  33. data/lib/bibliothecary/parsers/go.rb +51 -43
  34. data/lib/bibliothecary/parsers/haxelib.rb +2 -1
  35. data/lib/bibliothecary/parsers/julia.rb +5 -1
  36. data/lib/bibliothecary/parsers/maven.rb +93 -77
  37. data/lib/bibliothecary/parsers/meteor.rb +2 -0
  38. data/lib/bibliothecary/parsers/npm.rb +97 -34
  39. data/lib/bibliothecary/parsers/nuget.rb +37 -28
  40. data/lib/bibliothecary/parsers/packagist.rb +21 -11
  41. data/lib/bibliothecary/parsers/pub.rb +4 -2
  42. data/lib/bibliothecary/parsers/pypi.rb +48 -37
  43. data/lib/bibliothecary/parsers/rubygems.rb +16 -12
  44. data/lib/bibliothecary/parsers/shard.rb +10 -7
  45. data/lib/bibliothecary/purl_util.rb +2 -4
  46. data/lib/bibliothecary/related_files_info.rb +7 -8
  47. data/lib/bibliothecary/runner/multi_manifest_filter.rb +5 -4
  48. data/lib/bibliothecary/runner.rb +12 -10
  49. data/lib/bibliothecary/version.rb +3 -1
  50. data/lib/bibliothecary.rb +7 -4
  51. data/lib/sdl_parser.rb +11 -6
  52. metadata +18 -120
  53. data/lib/bibliothecary/parsers/carthage.rb +0 -52
  54. data/lib/bibliothecary/parsers/clojars.rb +0 -38
  55. data/lib/bibliothecary/parsers/hackage.rb +0 -53
  56. data/lib/bibliothecary/parsers/hex.rb +0 -54
  57. data/lib/bibliothecary/parsers/swift_pm.rb +0 -35
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "json"
2
4
 
3
5
  module Bibliothecary
@@ -37,29 +39,29 @@ module Bibliothecary
37
39
  add_multi_parser(Bibliothecary::MultiParsers::Spdx)
38
40
  add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
39
41
 
40
- def self.parse_package_lock(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
42
+ def self.parse_package_lock(file_contents, options: {})
41
43
  manifest = JSON.parse(file_contents)
42
44
  # https://docs.npmjs.com/cli/v9/configuring-npm/package-lock-json#lockfileversion
43
45
  if manifest["lockfileVersion"].to_i <= 1
44
46
  # lockfileVersion 1 uses the "dependencies" object
45
- parse_package_lock_v1(manifest)
47
+ parse_package_lock_v1(manifest, options.fetch(:filename, nil))
46
48
  else
47
49
  # lockfileVersion 2 has backwards-compatability by including both "packages" and the legacy "dependencies" object
48
50
  # lockfileVersion 3 has no backwards-compatibility and only includes the "packages" object
49
- parse_package_lock_v2(manifest)
51
+ parse_package_lock_v2(manifest, options.fetch(:filename, nil))
50
52
  end
51
53
  end
52
54
 
53
55
  class << self
54
56
  # "package-lock.json" and "npm-shrinkwrap.json" have same format, so use same parsing logic
55
- alias_method :parse_shrinkwrap, :parse_package_lock
57
+ alias parse_shrinkwrap parse_package_lock
56
58
  end
57
59
 
58
- def self.parse_package_lock_v1(manifest)
59
- parse_package_lock_deps_recursively(manifest.fetch("dependencies", []))
60
+ def self.parse_package_lock_v1(manifest, source = nil)
61
+ parse_package_lock_deps_recursively(manifest.fetch("dependencies", []), source)
60
62
  end
61
63
 
62
- def self.parse_package_lock_v2(manifest)
64
+ def self.parse_package_lock_v2(manifest, source = nil)
63
65
  # "packages" is a flat object where each key is the installed location of the dep, e.g. node_modules/foo/node_modules/bar.
64
66
  manifest
65
67
  .fetch("packages")
@@ -73,33 +75,39 @@ module Bibliothecary
73
75
  Dependency.new(
74
76
  name: name.split("node_modules/").last,
75
77
  requirement: dep["version"],
76
- type: dep.fetch("dev", false) || dep.fetch("devOptional", false) ? "development" : "runtime",
78
+ type: dep.fetch("dev", false) || dep.fetch("devOptional", false) ? "development" : "runtime",
77
79
  local: dep.fetch("link", false),
80
+ source: source
78
81
  )
79
82
  end
80
83
  end
81
84
 
82
- def self.parse_package_lock_deps_recursively(dependencies, depth=1)
85
+ def self.parse_package_lock_deps_recursively(dependencies, source = nil, depth = 1)
83
86
  dependencies.flat_map do |name, requirement|
84
87
  type = requirement.fetch("dev", false) ? "development" : "runtime"
85
88
  version = requirement.key?("from") ? requirement["from"][/#(?:semver:)?v?(.*)/, 1] : nil
86
89
  version ||= requirement["version"].split("#").last
87
90
  child_dependencies = if depth >= PACKAGE_LOCK_JSON_MAX_DEPTH
88
- []
89
- else
90
- parse_package_lock_deps_recursively(requirement.fetch("dependencies", []), depth + 1)
91
+ []
92
+ else
93
+ parse_package_lock_deps_recursively(requirement.fetch("dependencies", []), source, depth + 1)
91
94
  end
92
95
 
93
96
  [Dependency.new(
94
97
  name: name,
95
98
  requirement: version,
96
99
  type: type,
100
+ source: source
97
101
  )] + child_dependencies
98
102
  end
99
103
  end
100
104
 
101
- def self.parse_manifest(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
105
+ def self.parse_manifest(file_contents, options: {})
106
+ # on ruby 3.2 we suddenly get this JSON error, so detect and return early: "package.json: unexpected token at ''"
107
+ return [] if file_contents.empty?
108
+
102
109
  manifest = JSON.parse(file_contents)
110
+
103
111
  raise "appears to be a lockfile rather than manifest format" if manifest.key?("lockfileVersion")
104
112
 
105
113
  dependencies = manifest.fetch("dependencies", [])
@@ -109,7 +117,8 @@ module Bibliothecary
109
117
  name: name,
110
118
  requirement: requirement,
111
119
  type: "runtime",
112
- local: requirement.start_with?("file:")
120
+ local: requirement.start_with?("file:"),
121
+ source: options.fetch(:filename, nil)
113
122
  )
114
123
  end
115
124
 
@@ -120,38 +129,91 @@ module Bibliothecary
120
129
  name: name,
121
130
  requirement: requirement,
122
131
  type: "development",
123
- local: requirement.start_with?("file:")
132
+ local: requirement.start_with?("file:"),
133
+ source: options.fetch(:filename, nil)
124
134
  )
125
135
  end
126
136
 
127
137
  dependencies
128
138
  end
129
139
 
130
- def self.parse_yarn_lock(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
131
- response = Typhoeus.post("#{Bibliothecary.configuration.yarn_parser_host}/parse", body: file_contents)
140
+ def self.parse_yarn_lock(file_contents, options: {})
141
+ dep_hash = if file_contents.match(/__metadata:/)
142
+ parse_v2_yarn_lock(file_contents, options.fetch(:filename, nil))
143
+ else
144
+ parse_v1_yarn_lock(file_contents, options.fetch(:filename, nil))
145
+ end
146
+
147
+ dep_hash.map do |dep|
148
+ Dependency.new(
149
+ name: dep[:name],
150
+ requirement: dep[:version],
151
+ type: "runtime", # lockfile doesn't tell us more about the type of dep
152
+ local: dep[:requirements]&.first&.start_with?("file:"),
153
+ source: options.fetch(:filename, nil)
154
+ )
155
+ end
156
+ end
132
157
 
133
- raise Bibliothecary::RemoteParsingError.new("Http Error #{response.response_code} when contacting: #{Bibliothecary.configuration.yarn_parser_host}/parse", response.response_code) unless response.success?
158
+ # Returns a hash representation of the deps in yarn.lock, eg:
159
+ # [{
160
+ # name: "foo",
161
+ # requirements: [["foo", "^1.0.0"], ["foo", "^1.0.1"]],
162
+ # version: "1.2.0",
163
+ # }, ...]
164
+ def self.parse_v1_yarn_lock(contents, source = nil)
165
+ contents
166
+ .gsub(/^#.*/, "")
167
+ .strip
168
+ .split("\n\n")
169
+ .map do |chunk|
170
+ requirements = chunk
171
+ .lines
172
+ .find { |l| !l.start_with?(" ") && l.strip.end_with?(":") } # first line, eg: '"@bar/foo@1.0.0", "@bar/foo@^1.0.1":'
173
+ .strip
174
+ .gsub(/"|:$/, "") # don't need quotes or trailing colon
175
+ .split(",") # split the list of requirements
176
+ .map { |d| d.strip.split(/(?<!^)@/, 2) } # split each requirement on name/version "@"", not on leading namespace "@"
177
+ version = chunk.match(/version "?([^"]*)"?/)[1]
178
+
179
+ {
180
+ name: requirements.first.first,
181
+ requirements: requirements.map { |x| x[1] },
182
+ version: version,
183
+ source: source,
184
+ }
185
+ end
186
+ end
134
187
 
135
- json = JSON.parse(response.body, symbolize_names: true)
136
- json
137
- .uniq
138
- .reject do |dep|
139
- dep[:requirement]&.include?("workspace") && dep[:version].include?("use.local")
188
+ def self.parse_v2_yarn_lock(contents, source = nil)
189
+ parsed = YAML.load(contents)
190
+ parsed = parsed.except("__metadata")
191
+ parsed
192
+ .reject do |packages, info|
193
+ # yarn v4+ creates a lockfile entry: "myproject@workspace" with a "use.local" version
194
+ # this lockfile entry is a reference to the project to which the lockfile belongs
195
+ # skip this self-referential package
196
+ info["version"].to_s.include?("use.local") && packages.include?("workspace")
140
197
  end
141
- .map do |dep|
142
- Dependency.new(
143
- name: dep[:name],
144
- requirement: dep[:version],
145
- type: dep[:type],
146
- local: dep[:requirement]&.start_with?("file:"),
147
- )
198
+ .map do |packages, info|
199
+ packages = packages.split(", ")
200
+ # use first requirement's name, assuming that deps will always resolve from deps of the same name
201
+ name = packages.first.rpartition("@").first
202
+ requirements = packages.map { |p| p.rpartition("@").last.gsub(/^.*:/, "") }
203
+
204
+ {
205
+ name: name,
206
+ requirements: requirements,
207
+ version: info["version"].to_s,
208
+ source: source,
209
+ }
148
210
  end
149
211
  end
150
212
 
151
- def self.parse_ls(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
213
+ def self.parse_ls(file_contents, options: {})
152
214
  manifest = JSON.parse(file_contents)
153
215
 
154
- transform_tree_to_array(manifest.fetch("dependencies", {}))
216
+ transform_tree_to_array(manifest.fetch("dependencies", {}), options.fetch(:filename, nil))
155
217
  end
156
218
 
157
219
  def self.lockfile_preference_order(file_infos)
@@ -166,15 +228,16 @@ module Bibliothecary
166
228
  end
167
229
  end
168
230
 
169
- private_class_method def self.transform_tree_to_array(deps_by_name)
231
+ private_class_method def self.transform_tree_to_array(deps_by_name, source = nil)
170
232
  deps_by_name.map do |name, metadata|
171
233
  [
172
234
  Dependency.new(
173
235
  name: name,
174
236
  requirement: metadata["version"],
175
237
  type: "runtime",
238
+ source: source
176
239
  ),
177
- ] + transform_tree_to_array(metadata.fetch("dependencies", {}))
240
+ ] + transform_tree_to_array(metadata.fetch("dependencies", {}), source)
178
241
  end.flatten(1)
179
242
  end
180
243
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "ox"
2
4
  require "json"
3
5
 
@@ -48,23 +50,24 @@ module Bibliothecary
48
50
  add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
49
51
  add_multi_parser(Bibliothecary::MultiParsers::Spdx)
50
52
 
51
- def self.parse_project_lock_json(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
53
+ def self.parse_project_lock_json(file_contents, options: {})
52
54
  manifest = JSON.parse file_contents
53
- manifest.fetch("libraries",[]).map do |name, _requirement|
55
+ manifest.fetch("libraries", []).map do |name, _requirement|
54
56
  dep = name.split("/")
55
57
  Dependency.new(
56
58
  name: dep[0],
57
59
  requirement: dep[1],
58
60
  type: "runtime",
61
+ source: options.fetch(:filename, nil)
59
62
  )
60
63
  end
61
64
  end
62
65
 
63
- def self.parse_packages_lock_json(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
66
+ def self.parse_packages_lock_json(file_contents, options: {})
64
67
  manifest = JSON.parse file_contents
65
68
 
66
69
  frameworks = {}
67
- manifest.fetch("dependencies",[]).each do |framework, deps|
70
+ manifest.fetch("dependencies", []).each do |framework, deps|
68
71
  frameworks[framework] = deps
69
72
  .reject { |_name, details| details["type"] == "Project" } # Projects do not have versions
70
73
  .map do |name, details|
@@ -74,44 +77,46 @@ module Bibliothecary
74
77
  # so fallback to requested is pure paranoia
75
78
  requirement: details.fetch("resolved", details.fetch("requested", "*")),
76
79
  type: "runtime",
80
+ source: options.fetch(:filename, nil)
77
81
  )
78
82
  end
79
83
  end
80
84
 
81
- if frameworks.size > 0
85
+ unless frameworks.empty?
82
86
  # we should really return multiple manifests, but bibliothecary doesn't
83
87
  # do that yet so at least pick deterministically.
84
88
 
85
89
  # Note, frameworks can be empty, so remove empty ones and then return the last sorted item if any
86
90
  frameworks = frameworks.delete_if { |_k, v| v.empty? }
87
- return frameworks[frameworks.keys.sort.last] unless frameworks.empty?
91
+ return frameworks[frameworks.keys.max] unless frameworks.empty?
88
92
  end
89
93
  []
90
94
  end
91
95
 
92
- def self.parse_packages_config(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
96
+ def self.parse_packages_config(file_contents, options: {})
93
97
  manifest = Ox.parse file_contents
94
98
  manifest.packages.locate("package").map do |dependency|
95
99
  Dependency.new(
96
100
  name: dependency.id,
97
101
  requirement: (dependency.version if dependency.respond_to? "version"),
98
102
  type: dependency.respond_to?("developmentDependency") && dependency.developmentDependency == "true" ? "development" : "runtime",
103
+ source: options.fetch(:filename, nil)
99
104
  )
100
105
  end
101
- rescue
106
+ rescue StandardError
102
107
  []
103
108
  end
104
109
 
105
- def self.parse_csproj(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
110
+ def self.parse_csproj(file_contents, options: {})
106
111
  manifest = Ox.parse file_contents
107
112
 
108
- packages = manifest.locate("ItemGroup/PackageReference").select{ |dep| dep.respond_to? "Include" }.map do |dependency|
113
+ packages = manifest.locate("ItemGroup/PackageReference").select { |dep| dep.respond_to? "Include" }.map do |dependency|
109
114
  requirement = (dependency.Version if dependency.respond_to? "Version")
110
115
  if requirement.is_a?(Ox::Element)
111
- requirement = dependency.nodes.detect{ |n| n.value == "Version" }&.text
116
+ requirement = dependency.nodes.detect { |n| n.value == "Version" }&.text
112
117
  end
113
118
 
114
- type = if dependency.nodes.first && dependency.nodes.first.nodes.include?("all") && dependency.nodes.first.value.include?("PrivateAssets") || dependency.attributes[:PrivateAssets] == "All"
119
+ type = if (dependency.nodes.first&.nodes&.include?("all") && dependency.nodes.first.value.include?("PrivateAssets")) || dependency.attributes[:PrivateAssets] == "All"
115
120
  "development"
116
121
  else
117
122
  "runtime"
@@ -121,27 +126,29 @@ module Bibliothecary
121
126
  name: dependency.Include,
122
127
  requirement: requirement,
123
128
  type: type,
129
+ source: options.fetch(:filename, nil)
124
130
  )
125
131
  end
126
- packages.uniq {|package| package.name }
127
- rescue
132
+ packages.uniq(&:name)
133
+ rescue StandardError
128
134
  []
129
135
  end
130
136
 
131
- def self.parse_nuspec(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
137
+ def self.parse_nuspec(file_contents, options: {})
132
138
  manifest = Ox.parse file_contents
133
139
  manifest.package.metadata.dependencies.locate("dependency").map do |dependency|
134
140
  Dependency.new(
135
141
  name: dependency.id,
136
142
  requirement: dependency.attributes[:version],
137
143
  type: dependency.respond_to?("developmentDependency") && dependency.developmentDependency == "true" ? "development" : "runtime",
144
+ source: options.fetch(:filename, nil)
138
145
  )
139
146
  end
140
- rescue
147
+ rescue StandardError
141
148
  []
142
149
  end
143
150
 
144
- def self.parse_paket_lock(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
151
+ def self.parse_paket_lock(file_contents, options: {})
145
152
  lines = file_contents.split("\n")
146
153
  package_version_re = /\s+(?<name>\S+)\s\((?<version>\d+\.\d+[\.\d+[\.\d+]*]*)\)/
147
154
  packages = lines.select { |line| package_version_re.match(line) }.map { |line| package_version_re.match(line) }.map do |match|
@@ -149,36 +156,38 @@ module Bibliothecary
149
156
  name: match[:name].strip,
150
157
  requirement: match[:version],
151
158
  type: "runtime",
159
+ source: options.fetch(:filename, nil)
152
160
  )
153
161
  end
154
162
  # we only have to enforce uniqueness by name because paket ensures that there is only the single version globally in the project
155
- packages.uniq {|package| package.name }
163
+ packages.uniq(&:name)
156
164
  end
157
165
 
158
- def self.parse_project_assets_json(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
166
+ def self.parse_project_assets_json(file_contents, options: {})
159
167
  manifest = JSON.parse file_contents
160
168
 
161
169
  frameworks = {}
162
- manifest.fetch("targets",[]).each do |framework, deps|
170
+ manifest.fetch("targets", []).each do |framework, deps|
163
171
  frameworks[framework] = deps
164
172
  .select { |_name, details| details["type"] == "package" }
165
173
  .map do |name, _details|
166
- name_split = name.split("/")
167
- Dependency.new(
168
- name: name_split[0],
169
- requirement: name_split[1],
170
- type: "runtime",
171
- )
174
+ name_split = name.split("/")
175
+ Dependency.new(
176
+ name: name_split[0],
177
+ requirement: name_split[1],
178
+ type: "runtime",
179
+ source: options.fetch(:filename, nil)
180
+ )
172
181
  end
173
182
  end
174
183
 
175
- if frameworks.size > 0
184
+ unless frameworks.empty?
176
185
  # we should really return multiple manifests, but bibliothecary doesn't
177
186
  # do that yet so at least pick deterministically.
178
187
 
179
188
  # Note, frameworks can be empty, so remove empty ones and then return the last sorted item if any
180
189
  frameworks = frameworks.delete_if { |_k, v| v.empty? }
181
- return frameworks[frameworks.keys.sort.last] unless frameworks.empty?
190
+ return frameworks[frameworks.keys.max] unless frameworks.empty?
182
191
  end
183
192
  []
184
193
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "json"
2
4
 
3
5
  module Bibliothecary
@@ -22,39 +24,47 @@ module Bibliothecary
22
24
  add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
23
25
  add_multi_parser(Bibliothecary::MultiParsers::Spdx)
24
26
 
25
- def self.parse_lockfile(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
27
+ def self.parse_lockfile(file_contents, options: {})
26
28
  manifest = JSON.parse file_contents
27
- manifest.fetch("packages",[]).map do |dependency|
29
+ manifest.fetch("packages", []).map do |dependency|
28
30
  requirement = dependency["version"]
29
31
 
30
32
  # Store Drupal version if Drupal, but include the original manifest version for reference
31
- original_requirement, requirement = requirement, dependency.dig("source", "reference") if is_drupal_module(dependency)
33
+ if drupal_module?(dependency)
34
+ original_requirement = requirement
35
+ requirement = dependency.dig("source", "reference")
36
+ end
32
37
 
33
38
  Dependency.new(
34
39
  name: dependency["name"],
35
40
  requirement: requirement,
36
41
  type: "runtime",
37
- original_requirement: original_requirement
42
+ original_requirement: original_requirement,
43
+ source: options.fetch(:filename, nil)
38
44
  )
39
- end + manifest.fetch("packages-dev",[]).map do |dependency|
45
+ end + manifest.fetch("packages-dev", []).map do |dependency|
40
46
  requirement = dependency["version"]
41
47
 
42
48
  # Store Drupal version if Drupal, but include the original manifest version for reference
43
- original_requirement, requirement = requirement, dependency.dig("source", "reference") if is_drupal_module(dependency)
49
+ if drupal_module?(dependency)
50
+ original_requirement = requirement
51
+ requirement = dependency.dig("source", "reference")
52
+ end
44
53
 
45
54
  Dependency.new(
46
55
  name: dependency["name"],
47
56
  requirement: requirement,
48
57
  type: "development",
49
- original_requirement: original_requirement
58
+ original_requirement: original_requirement,
59
+ source: options.fetch(:filename, nil)
50
60
  )
51
61
  end
52
62
  end
53
63
 
54
- def self.parse_manifest(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
64
+ def self.parse_manifest(file_contents, options: {})
55
65
  manifest = JSON.parse file_contents
56
- map_dependencies(manifest, "require", "runtime") +
57
- map_dependencies(manifest, "require-dev", "development")
66
+ map_dependencies(manifest, "require", "runtime", options.fetch(:filename, nil)) +
67
+ map_dependencies(manifest, "require-dev", "development", options.fetch(:filename, nil))
58
68
  end
59
69
 
60
70
  # Drupal hosts its own Composer repository, where its "modules" are indexed and searchable. The best way to
@@ -65,7 +75,7 @@ module Bibliothecary
65
75
  # (https://www.drupal.org/project/project_composer/issues/2622450),
66
76
  # so we return the Drupal requirement instead of semver requirement if it's here
67
77
  # (https://www.drupal.org/docs/develop/using-composer/using-composer-to-install-drupal-and-manage-dependencies#s-about-semantic-versioning)
68
- private_class_method def self.is_drupal_module(dependency)
78
+ private_class_method def self.drupal_module?(dependency)
69
79
  dependency["type"] =~ /drupal/ && dependency.dig("source", "reference")
70
80
  end
71
81
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "yaml"
2
4
 
3
5
  module Bibliothecary
@@ -23,7 +25,7 @@ module Bibliothecary
23
25
  def self.parse_yaml_manifest(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
24
26
  manifest = YAML.load file_contents
25
27
  map_dependencies(manifest, "dependencies", "runtime") +
26
- map_dependencies(manifest, "dev_dependencies", "development")
28
+ map_dependencies(manifest, "dev_dependencies", "development")
27
29
  end
28
30
 
29
31
  def self.parse_yaml_lockfile(file_contents, options: {}) # rubocop:disable Lint/UnusedMethodArgument
@@ -32,7 +34,7 @@ module Bibliothecary
32
34
  Dependency.new(
33
35
  name: name,
34
36
  requirement: dep["version"],
35
- type: "runtime",
37
+ type: "runtime"
36
38
  )
37
39
  end
38
40
  end