-
Notifications
You must be signed in to change notification settings - Fork 202
/
npm.rb
174 lines (149 loc) · 5.8 KB
/
npm.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# frozen_string_literal: true
module PackageManager
class NPM < Base
HAS_VERSIONS = true
HAS_DEPENDENCIES = true
BIBLIOTHECARY_SUPPORT = true
SECURITY_PLANNED = true
URL = "https://www.npmjs.com"
COLOR = "#f1e05a"
ENTIRE_PACKAGE_CAN_BE_DEPRECATED = true
def self.missing_version_remover
PackageManager::Base::MissingVersionRemover
end
def self.package_link(db_project, _version = nil)
"https://www.npmjs.com/package/#{db_project.name}"
end
def self.download_url(db_project, version = nil)
"https://registry.npmjs.org/#{db_project.name}/-/#{db_project.name}-#{version}.tgz"
end
def self.check_status_url(db_project)
"https://registry.npmjs.org/#{db_project.name.gsub('/', '%2F')}"
end
def self.install_instructions(db_project, version = nil)
"npm install #{db_project.name}" + (version ? "@#{version}" : "")
end
def self.formatted_name
"npm"
end
def self.project_names
get("https://raw.githubusercontent.com/nice-registry/all-the-package-names/master/names.json")
end
def self.recent_names
u = "http://registry.npmjs.org/-/rss?descending=true&limit=50"
SimpleRSS.parse(get_raw(u)).items.map(&:title).uniq
end
def self.project(name)
get("https://registry.npmjs.org/#{name.gsub('/', '%2F')}")
end
def self.deprecation_info(db_project)
# NPM only allows versions to be deprecated (not the whole package), so we count a package deprecated
# on NPM if the latest version is deprecated (vs requiring all versions to be deprecated), because
# the package page will say it's deprecated if even the latest version is actually deprecated.
versions = project(db_project.name)&.dig("versions")&.values || []
last_stable_version = versions
# Ignore prerelease versions as some packages will regularly push prereleases and mark them deprecated, e.g. graphql.
.reject do |v|
Semantic::Version.new(v["version"]).pre.present?
rescue StandardError
false
end
.last
message = last_stable_version&.dig("deprecated")
# sometimes message is a boolean `false`, and it may be `true` sometimes (not sure but
# may as well handle it, apparently we return any type we like)
if message.nil?
is_deprecated = false
elsif [true, false].include?(message)
is_deprecated = message
message = nil
elsif message.is_a? String
is_deprecated = true
else
is_deprecated = false
end
{
is_deprecated: is_deprecated,
message: message,
}
end
def self.mapping(raw_project)
latest_version = if raw_project["versions"].present?
raw_project["versions"].to_a.last[1]
else
{} # "Removed" projects won't have a "versions" Hash
end
repo = raw_project.fetch("repository", {}).presence || latest_version.fetch("repository", {})
repo = repo[0] if repo.is_a?(Array)
repo_url = repo.try(:fetch, "url", nil)
MappingBuilder.build_hash(
name: raw_project["name"],
description: latest_version["description"],
homepage: raw_project["homepage"],
keywords_array: Array.wrap(latest_version.fetch("keywords", [])),
licenses: licenses(latest_version),
repository_url: repo_fallback(repo_url, raw_project["homepage"]),
versions: raw_project.fetch("versions", {})
)
end
def self.licenses(latest_version)
license = latest_version.fetch("license", nil)
if license.present?
if license.is_a?(Hash)
license.fetch("type", "")
else
license
end
else
licenses = Array(latest_version.fetch("licenses", []))
licenses.map do |lice|
if lice.is_a?(Hash)
lice.fetch("type", "")
else
lice
end
end.join(",")
end
end
def self.versions(raw_project, _name)
# npm license fields are supposed to be SPDX expressions now https://docs.npmjs.com/files/package.json#license
return [] if raw_project.nil?
raw_project.fetch("versions", {}).map do |k, v|
license = v.fetch("license", nil)
license = licenses(v) unless license.is_a?(String)
license = "" if license.nil?
VersionBuilder.build_hash(
number: k,
published_at: raw_project.fetch("time", {}).fetch(k, nil),
original_license: license
)
end
end
# NPM is currently unreliable in its update publishing.
# The updates are coming in out of order, which is throwing off single version updates.
# See https://github.com/librariesio/libraries.io/pull/3278 for more details.
def self.supports_single_version_update?
false
end
def self.one_version(raw_project, version_string)
versions(raw_project, raw_project["name"])
.find { |v| v[:number] == version_string }
end
def self.dependencies(name, version, mapped_project)
vers = mapped_project.fetch(:versions, {})[version]
if vers.nil?
StructuredLog.capture("DEPENDENCIES_FAILURE", { platform: db_platform, name: name, version: version, message: "version not found in upstream" })
return []
end
deps = map_dependencies(vers.fetch("dependencies", {}), "runtime") +
map_dependencies(vers.fetch("devDependencies", {}), "Development") +
map_dependencies(vers.fetch("optionalDependencies", {}), "Optional", optional: true)
deps.each do |d|
# Via https://docs.npmjs.com/cli/v9/configuring-npm/package-json#dependencies:
# `"" (just an empty string) Same as *`
d[:requirements] = "*" if d[:requirements].blank?
end
deps
end
end
end