bibliothecary 11.0.1 → 12.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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