bibliothecary 12.1.2 → 12.1.3
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.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/lib/bibliothecary/parsers/maven.rb +77 -22
- data/lib/bibliothecary/parsers/npm.rb +57 -5
- data/lib/bibliothecary/parsers/nuget.rb +2 -2
- data/lib/bibliothecary/parsers/pypi.rb +1 -1
- data/lib/bibliothecary/runner.rb +2 -2
- data/lib/bibliothecary/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b061a0f0ac234c87fb6821f0575ecb1f4fe7b9a217fdeec726cf5d9389a6264a
|
4
|
+
data.tar.gz: f4479d3b94254de19b34f0e0773ef6fdd4459b7fe388fe80a1567686b70c2a34
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8ba28c715feabd5561329e72361c7b01aae12987ddd1bd338c77d31e079b220c2ec0f08e8d03ac9386c08196b3d77a4bd8a5b01a18bff1c432efcae9034d00b8
|
7
|
+
data.tar.gz: 97d7c4fadc853771ca46a4a08da9d79c393d8c7048fc0dc867685115d632e0178fdc18fc6964e8aa9ad5c737d706ac4ba0ca0425ef7bdf483b9c8b6191d1ce98
|
data/Gemfile
CHANGED
@@ -254,34 +254,89 @@ module Bibliothecary
|
|
254
254
|
.uniq
|
255
255
|
end
|
256
256
|
|
257
|
-
|
258
|
-
|
257
|
+
# Return each item in the ascii art tree with a depth of that item,
|
258
|
+
# like [[0, "groupId:artifactId:jar:version:scope"], [1, "..."], ...]
|
259
|
+
# The depth-0 items are the (sub)project names
|
260
|
+
# These are in the original order, with no de-duplication.
|
261
|
+
def self.parse_maven_tree_items_with_depths(file_contents)
|
262
|
+
file_contents
|
259
263
|
.gsub(ANSI_MATCHER, "")
|
260
264
|
.gsub(/\r\n?/, "\n")
|
261
|
-
|
262
|
-
|
263
|
-
.
|
265
|
+
# capture two groups; one is the ASCII art telling us the tree depth,
|
266
|
+
# and two is the actual dependency
|
267
|
+
.scan(/^\[INFO\]\s((?:[-+|\\]|\s)*)((?:[\w\.-]+:)+[\w\.\-${}]+)/)
|
268
|
+
# lines that start with "-" aren't part of the tree, example: "[INFO] --- dependency:3.8.1:tree"
|
269
|
+
.reject { |(tree_ascii_art, _dep_info)| tree_ascii_art.start_with?("-") }
|
270
|
+
.map do |(tree_ascii_art, dep_info)|
|
271
|
+
child_marker_index = tree_ascii_art.index(/(\+-)|(\\-)/)
|
272
|
+
depth = if child_marker_index.nil?
|
273
|
+
0
|
274
|
+
else
|
275
|
+
# There are three characters present in the line for each level of depth
|
276
|
+
(child_marker_index / 3) + 1
|
277
|
+
end
|
278
|
+
[depth, dep_info]
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
# split "org.yaml:snakeyaml:jar:2.2:compile" into
|
283
|
+
# ["org.yaml:snakeyaml", "2.2", "compile"]
|
284
|
+
def self.parse_maven_tree_dependency(item)
|
285
|
+
parts = item.split(":")
|
286
|
+
case parts.count
|
287
|
+
when 4
|
288
|
+
version = parts[-1]
|
289
|
+
type = parts[-2]
|
290
|
+
when 5..6
|
291
|
+
version, type = parts[-2..]
|
292
|
+
end
|
293
|
+
|
294
|
+
name = parts[0..1].join(":")
|
295
|
+
|
296
|
+
[name, version, type]
|
297
|
+
end
|
298
|
+
|
299
|
+
def self.parse_maven_tree(file_contents, options: {})
|
300
|
+
keep_subprojects = options.fetch(:keep_subprojects_in_maven_tree, false)
|
301
|
+
|
302
|
+
items = parse_maven_tree_items_with_depths(file_contents)
|
303
|
+
|
304
|
+
raise "found no lines with deps in maven-dependency-tree.txt" if items.empty?
|
264
305
|
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
306
|
+
projects = {}
|
307
|
+
|
308
|
+
if keep_subprojects
|
309
|
+
# traditional behavior: we only exclude the root project, and only if we parsed multiple lines
|
310
|
+
(root_name, root_version, _root_type) = parse_maven_tree_dependency(items.shift[1])
|
311
|
+
unless items.empty?
|
312
|
+
projects[root_name] = Set.new
|
313
|
+
projects[root_name].add(root_version)
|
273
314
|
end
|
274
|
-
Dependency.new(
|
275
|
-
name: parts[0..1].join(":"),
|
276
|
-
requirement: version,
|
277
|
-
type: type,
|
278
|
-
source: options.fetch(:filename, nil)
|
279
|
-
)
|
280
315
|
end
|
281
316
|
|
282
|
-
|
283
|
-
|
284
|
-
|
317
|
+
unique_items = items.map do |(depth, item)|
|
318
|
+
(name, version, type) = parse_maven_tree_dependency(item)
|
319
|
+
if depth == 0 && !keep_subprojects
|
320
|
+
# record and then remove the depth 0
|
321
|
+
projects[name] ||= Set.new
|
322
|
+
projects[name].add(version)
|
323
|
+
nil
|
324
|
+
else
|
325
|
+
[name, version, type]
|
326
|
+
end
|
327
|
+
end.compact.uniq
|
328
|
+
|
329
|
+
unique_items
|
330
|
+
# drop the projects and subprojects
|
331
|
+
.reject { |(name, version, _type)| projects[name]&.include?(version) }
|
332
|
+
.map do |(name, version, type)|
|
333
|
+
Bibliothecary::Dependency.new(
|
334
|
+
name: name,
|
335
|
+
requirement: version,
|
336
|
+
type: type,
|
337
|
+
source: options.fetch(:filename, nil)
|
338
|
+
)
|
339
|
+
end
|
285
340
|
end
|
286
341
|
|
287
342
|
def self.parse_resolved_dep_line(line, options: {})
|
@@ -72,9 +72,22 @@ module Bibliothecary
|
|
72
72
|
# * The other occurrence's name is the path to the local dependency (which has less information, and is duplicative, so we discard)
|
73
73
|
.select { |name, _dep| name.start_with?("node_modules") }
|
74
74
|
.map do |name, dep|
|
75
|
+
# check if the name property is available and differs from the node modules location
|
76
|
+
# this indicates that the package has been aliased
|
77
|
+
node_module_name = name.split("node_modules/").last
|
78
|
+
name_property = dep["name"]
|
79
|
+
if !name_property.nil? && node_module_name != name_property
|
80
|
+
name = name_property
|
81
|
+
original_name = node_module_name
|
82
|
+
else
|
83
|
+
name = node_module_name
|
84
|
+
end
|
85
|
+
|
75
86
|
Dependency.new(
|
76
|
-
name: name
|
87
|
+
name: name,
|
88
|
+
original_name: original_name,
|
77
89
|
requirement: dep["version"],
|
90
|
+
original_requirement: original_name.nil? ? nil : dep["version"],
|
78
91
|
type: dep.fetch("dev", false) || dep.fetch("devOptional", false) ? "development" : "runtime",
|
79
92
|
local: dep.fetch("link", false),
|
80
93
|
source: source
|
@@ -113,9 +126,20 @@ module Bibliothecary
|
|
113
126
|
dependencies = manifest.fetch("dependencies", [])
|
114
127
|
.reject { |name, _requirement| name.start_with?("//") } # Omit comment keys. They are valid in package.json: https://groups.google.com/g/nodejs/c/NmL7jdeuw0M/m/yTqI05DRQrIJ
|
115
128
|
.map do |name, requirement|
|
129
|
+
# check to see if this is an aliased package name
|
130
|
+
# example: "alias-package-name": "npm:actual-package@^1.1.3"
|
131
|
+
if requirement.include?("npm:")
|
132
|
+
# the name of the real dependency is contained in the requirement with the version
|
133
|
+
requirement.gsub!("npm:", "")
|
134
|
+
original_name = name
|
135
|
+
name, _, requirement = requirement.rpartition("@")
|
136
|
+
end
|
137
|
+
|
116
138
|
Dependency.new(
|
117
139
|
name: name,
|
140
|
+
original_name: original_name,
|
118
141
|
requirement: requirement,
|
142
|
+
original_requirement: original_name.nil? ? nil : requirement,
|
119
143
|
type: "runtime",
|
120
144
|
local: requirement.start_with?("file:"),
|
121
145
|
source: options.fetch(:filename, nil)
|
@@ -147,7 +171,9 @@ module Bibliothecary
|
|
147
171
|
dep_hash.map do |dep|
|
148
172
|
Dependency.new(
|
149
173
|
name: dep[:name],
|
174
|
+
original_name: dep[:original_name],
|
150
175
|
requirement: dep[:version],
|
176
|
+
original_requirement: dep[:original_requirement],
|
151
177
|
type: "runtime", # lockfile doesn't tell us more about the type of dep
|
152
178
|
local: dep[:requirements]&.first&.start_with?("file:"),
|
153
179
|
source: options.fetch(:filename, nil)
|
@@ -173,12 +199,17 @@ module Bibliothecary
|
|
173
199
|
.strip
|
174
200
|
.gsub(/"|:$/, "") # don't need quotes or trailing colon
|
175
201
|
.split(",") # split the list of requirements
|
176
|
-
|
202
|
+
|
203
|
+
name, alias_name = yarn_strip_npm_protocol(requirements.first) # if a package is aliased, strip the alias and return the real package name
|
204
|
+
name = name.strip.split(/(?<!^)@/).first
|
205
|
+
requirements = requirements.map { |d| d.strip.split(/(?<!^)@/, 2) } # split each requirement on name/version "@"", not on leading namespace "@"
|
177
206
|
version = chunk.match(/version "?([^"]*)"?/)[1]
|
178
207
|
|
179
208
|
{
|
180
|
-
name:
|
209
|
+
name: name,
|
210
|
+
original_name: alias_name,
|
181
211
|
requirements: requirements.map { |x| x[1] },
|
212
|
+
original_requirement: alias_name.nil? ? nil : version,
|
182
213
|
version: version,
|
183
214
|
source: source,
|
184
215
|
}
|
@@ -193,17 +224,24 @@ module Bibliothecary
|
|
193
224
|
# yarn v4+ creates a lockfile entry: "myproject@workspace" with a "use.local" version
|
194
225
|
# this lockfile entry is a reference to the project to which the lockfile belongs
|
195
226
|
# skip this self-referential package
|
196
|
-
info["version"].to_s.include?("use.local") && packages.include?("workspace")
|
227
|
+
(info["version"].to_s.include?("use.local") && packages.include?("workspace")) ||
|
228
|
+
# yarn allows users to insert patches to their dependencies from within their project
|
229
|
+
# these patches are marked as a separate entry in the lock file but do not represent a new dependency
|
230
|
+
# and should be skipped here
|
231
|
+
# https://yarnpkg.com/protocol/patch
|
232
|
+
packages.include?("@patch:")
|
197
233
|
end
|
198
234
|
.map do |packages, info|
|
199
235
|
packages = packages.split(", ")
|
200
236
|
# use first requirement's name, assuming that deps will always resolve from deps of the same name
|
201
|
-
name = packages.first.rpartition("@").first
|
237
|
+
name, alias_name = yarn_strip_npm_protocol(packages.first.rpartition("@").first)
|
202
238
|
requirements = packages.map { |p| p.rpartition("@").last.gsub(/^.*:/, "") }
|
203
239
|
|
204
240
|
{
|
205
241
|
name: name,
|
242
|
+
original_name: alias_name,
|
206
243
|
requirements: requirements,
|
244
|
+
original_requirement: alias_name.nil? ? nil : info["version"].to_s,
|
207
245
|
version: info["version"].to_s,
|
208
246
|
source: source,
|
209
247
|
}
|
@@ -240,6 +278,20 @@ module Bibliothecary
|
|
240
278
|
] + transform_tree_to_array(metadata.fetch("dependencies", {}), source)
|
241
279
|
end.flatten(1)
|
242
280
|
end
|
281
|
+
|
282
|
+
# Yarn package names can be aliased by using the NPM protocol. If a package name includes
|
283
|
+
# the NPM protocol then the name following the @npm: protocol identifier is the name of the
|
284
|
+
# actual package being imported into the project under a different alias.
|
285
|
+
# https://classic.yarnpkg.com/lang/en/docs/cli/add/#toc-yarn-add-alias
|
286
|
+
private_class_method def self.yarn_strip_npm_protocol(dep_name)
|
287
|
+
if dep_name.include?("@npm:")
|
288
|
+
partitions = dep_name.rpartition("@npm:")
|
289
|
+
alias_name = partitions.first
|
290
|
+
dep_name = partitions.last
|
291
|
+
end
|
292
|
+
|
293
|
+
[dep_name, alias_name]
|
294
|
+
end
|
243
295
|
end
|
244
296
|
end
|
245
297
|
end
|
@@ -87,7 +87,7 @@ module Bibliothecary
|
|
87
87
|
# do that yet so at least pick deterministically.
|
88
88
|
|
89
89
|
# Note, frameworks can be empty, so remove empty ones and then return the last sorted item if any
|
90
|
-
frameworks
|
90
|
+
frameworks.delete_if { |_k, v| v.empty? }
|
91
91
|
return frameworks[frameworks.keys.max] unless frameworks.empty?
|
92
92
|
end
|
93
93
|
[]
|
@@ -186,7 +186,7 @@ module Bibliothecary
|
|
186
186
|
# do that yet so at least pick deterministically.
|
187
187
|
|
188
188
|
# Note, frameworks can be empty, so remove empty ones and then return the last sorted item if any
|
189
|
-
frameworks
|
189
|
+
frameworks.delete_if { |_k, v| v.empty? }
|
190
190
|
return frameworks[frameworks.keys.max] unless frameworks.empty?
|
191
191
|
end
|
192
192
|
[]
|
@@ -13,7 +13,7 @@ module Bibliothecary
|
|
13
13
|
REQUIRE_REGEXP = /([a-zA-Z0-9]+[a-zA-Z0-9\-_\.]+)(?:\[.*?\])*([><=\w\.,]+)?/
|
14
14
|
REQUIREMENTS_REGEXP = /^#{REQUIRE_REGEXP}/
|
15
15
|
|
16
|
-
MANIFEST_REGEXP = /.*require[^\/]
|
16
|
+
MANIFEST_REGEXP = /.*require[^\/]*\.(txt|pip|in)$/
|
17
17
|
# TODO: can this be a more specific regexp so it doesn't match something like ".yarn/cache/create-require-npm-1.0.0.zip"?
|
18
18
|
PIP_COMPILE_REGEXP = /.*require.*$/
|
19
19
|
|
data/lib/bibliothecary/runner.rb
CHANGED
@@ -5,11 +5,11 @@ module Bibliothecary
|
|
5
5
|
# A runner is created every time a file is targeted to be parsed. Don't call
|
6
6
|
# parse methods directory! Use a Runner.
|
7
7
|
class Runner
|
8
|
-
def initialize(configuration)
|
8
|
+
def initialize(configuration, parser_options: {})
|
9
9
|
@configuration = configuration
|
10
10
|
@options = {
|
11
11
|
cache: {},
|
12
|
-
}
|
12
|
+
}.merge(parser_options)
|
13
13
|
end
|
14
14
|
|
15
15
|
def analyse(path, ignore_unparseable_files: true)
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bibliothecary
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 12.1.
|
4
|
+
version: 12.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Nesbitt
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-
|
10
|
+
date: 2025-03-14 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: commander
|