Skip to content

Commit b8d1613

Browse files
committedSep 10, 2019
support: simplify to just collecting and showing URLs
1 parent b892bf8 commit b8d1613

File tree

6 files changed

+111
-299
lines changed

6 files changed

+111
-299
lines changed
 

‎lib/install.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ var unlock = locker.unlock
119119
var parseJSON = require('./utils/parse-json.js')
120120
var output = require('./utils/output.js')
121121
var saveMetrics = require('./utils/metrics.js').save
122+
var validSupportURL = require('./utils/valid-support-url')
122123

123124
// install specific libraries
124125
var copyTree = require('./install/copy-tree.js')
@@ -811,7 +812,9 @@ Installer.prototype.printInstalledForHuman = function (diffs, auditResult) {
811812
var mutation = action[0]
812813
var pkg = action[1]
813814
if (pkg.failed) return
814-
if (mutation !== 'remove' && pkg.package.support) {
815+
if (
816+
mutation !== 'remove' && validSupportURL(pkg.package.support)
817+
) {
815818
haveSupportable = true
816819
}
817820
if (mutation === 'remove') {

‎lib/support.js

+44-188
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
'use strict'
22

3-
var npm = require('./npm.js')
4-
var output = require('./utils/output.js')
5-
var readPackageTree = require('read-package-tree')
6-
var runParallelLimit = require('run-parallel-limit')
7-
var simpleGet = require('simple-get')
8-
var semver = require('semver')
9-
var hasANSI = require('has-ansi')
3+
const npm = require('./npm.js')
4+
const output = require('./utils/output.js')
5+
const path = require('path')
6+
const readPackageTree = require('read-package-tree')
7+
const semver = require('semver')
8+
const validSupportURL = require('./utils/valid-support-url')
109

1110
module.exports = support
1211

@@ -18,7 +17,6 @@ support.usage = usage(
1817

1918
support.completion = function (opts, cb) {
2019
const argv = opts.conf.argv.remain
21-
2220
switch (argv[2]) {
2321
case 'support':
2422
return cb(null, [])
@@ -27,206 +25,64 @@ support.completion = function (opts, cb) {
2725
}
2826
}
2927

28+
// Compare lib/ls.js.
3029
function support (args, silent, cb) {
31-
readPackageTree(npm.dir, function (error, tree) {
32-
if (error) return cb(error)
33-
var supportablePackages = Array.from(findSupportablePackages(tree))
34-
downloadSupportData(supportablePackages, function (error, data) {
35-
if (error) return cb(error)
36-
37-
if (typeof cb !== 'function') {
38-
cb = silent
39-
silent = false
40-
}
41-
if (silent) return cb(null, data)
42-
43-
var out
44-
var json = npm.config.get('json')
45-
if (json) {
46-
out = JSON.stringify(data, null, 2)
47-
} else {
48-
out = data
49-
.sort(function (a, b) {
50-
var comparison = a.name.localeCompare(b.name)
51-
return comparison === 0
52-
? semver.compare(a.version, b.version)
53-
: comparison
54-
})
55-
.map(displaySupportData)
56-
.join('\n\n')
57-
}
58-
output(out)
59-
if (error) process.exitCode = 1
60-
cb(error, data)
61-
})
30+
if (typeof cb !== 'function') {
31+
cb = silent
32+
silent = false
33+
}
34+
const dir = path.resolve(npm.dir, '..')
35+
readPackageTree(dir, function (err, tree) {
36+
if (err) {
37+
process.exitCode = 1
38+
return cb(err)
39+
}
40+
const data = findPackages(tree)
41+
if (silent) return cb(null, data)
42+
var out
43+
if (npm.config.get('json')) {
44+
out = JSON.stringify(data, null, 2)
45+
} else {
46+
out = data.map(displayPackage).join('\n\n')
47+
}
48+
output(out)
49+
cb(err, data)
6250
})
6351
}
6452

65-
function findSupportablePackages (root) {
66-
var set = new Set()
53+
function findPackages (root) {
54+
const set = new Set()
6755
iterate(root)
68-
return set
56+
return Array.from(set).sort(function (a, b) {
57+
const comparison = a.name
58+
.toLowerCase()
59+
.localeCompare(b.name.toLowerCase())
60+
return comparison === 0
61+
? semver.compare(a.version, b.version)
62+
: comparison
63+
})
6964

7065
function iterate (node) {
7166
node.children.forEach(recurse)
7267
}
7368

7469
function recurse (node) {
75-
var metadata = node.package
76-
if (metadata.support) {
70+
const metadata = node.package
71+
const support = metadata.support
72+
if (support && validSupportURL(support)) {
7773
set.add({
7874
name: metadata.name,
7975
version: metadata.version,
76+
path: node.path,
8077
homepage: metadata.homepage,
8178
repository: metadata.repository,
82-
support: metadata.support,
83-
parent: node.parent,
84-
path: node.path
79+
support: metadata.support
8580
})
8681
}
8782
if (node.children) iterate(node)
8883
}
8984
}
9085

91-
function downloadSupportData (supportablePackages, cb) {
92-
var cache = new Map()
93-
var headers = { 'user-agent': npm.config.get('user-agent') }
94-
runParallelLimit(supportablePackages.map(function (entry) {
95-
return function task (done) {
96-
var url = entry.support
97-
get(url, function (error, response, projectData) {
98-
if (error) {
99-
return done(null, {
100-
url: url,
101-
error: 'could not download data'
102-
})
103-
}
104-
if (typeof projectData !== 'object' || Array.isArray(projectData)) {
105-
return done(null, {
106-
url: url,
107-
error: 'not an object'
108-
})
109-
}
110-
var contributors = projectData.contributors
111-
if (!Array.isArray(contributors)) {
112-
return done(null, projectData)
113-
}
114-
runParallelLimit(contributors.map(function (contributor) {
115-
return function (done) {
116-
if (
117-
typeof contributor !== 'object' ||
118-
typeof contributor.url !== 'string'
119-
) {
120-
return setImmediate(function () {
121-
done(null, contributor)
122-
})
123-
}
124-
get(contributor.url, function (error, response, contributorData) {
125-
if (error) {
126-
return done(null, {
127-
url: contributor.url,
128-
error: error
129-
})
130-
}
131-
contributorData.url = contributor.url
132-
var result = {
133-
name: contributorData.name,
134-
type: contributorData.type,
135-
url: contributor.url
136-
}
137-
if (looksLikeURL(contributorData.homepage)) {
138-
result.homepage = contributorData.homepage
139-
}
140-
if (
141-
Array.isArray(contributorData.links) &&
142-
contributorData.links.every(function (element) {
143-
return looksLikeURL(element)
144-
})
145-
) {
146-
result.links = contributorData.links
147-
}
148-
done(null, result)
149-
})
150-
}
151-
}), 5, function (error, resolvedContributors) {
152-
if (error) return done(error)
153-
done(null, {
154-
name: entry.name,
155-
version: entry.version,
156-
url: entry.support,
157-
homepage: entry.homepage,
158-
contributors: resolvedContributors
159-
})
160-
})
161-
})
162-
}
163-
}), 5, cb)
164-
165-
function get (url, cb) {
166-
var cached = cache.get(url)
167-
if (cached) {
168-
return setImmediate(function () {
169-
cb(null, { cached: true }, cached)
170-
})
171-
}
172-
simpleGet.concat({
173-
url: url,
174-
json: true,
175-
headers: headers
176-
}, function (err, response, data) {
177-
if (err) return cb(err)
178-
cache.set(url, data)
179-
cb(null, response, data)
180-
})
181-
}
182-
}
183-
184-
function displaySupportData (entry) {
185-
var returned = [entry.name + '@' + entry.version]
186-
if (looksLikeURL(entry.homepage)) {
187-
returned[0] += ' (' + entry.homepage + ')'
188-
}
189-
if (Array.isArray(entry.contributors)) {
190-
entry.contributors.forEach(function (contributor) {
191-
var name = contributor.name
192-
if (looksLikeSafeString(name)) {
193-
var item = ['- ' + name]
194-
var email = contributor.email
195-
if (looksLikeSafeString(email)) {
196-
item[0] += ' <' + email + '>'
197-
}
198-
var homepage = contributor.homepage
199-
if (looksLikeURL(homepage)) {
200-
item[0] += ' (' + homepage + ')'
201-
}
202-
var links = contributor.links
203-
if (Array.isArray(links)) {
204-
links.forEach(function (link) {
205-
if (looksLikeURL(link)) item.push(' ' + link)
206-
})
207-
}
208-
returned.push(item.join('\n'))
209-
}
210-
})
211-
}
212-
return returned.join('\n')
213-
}
214-
215-
function looksLikeSafeString (argument) {
216-
return (
217-
typeof argument === 'string' &&
218-
argument.length > 0 &&
219-
argument.length < 80 &&
220-
!hasANSI(argument)
221-
)
222-
}
223-
224-
function looksLikeURL (argument) {
225-
return (
226-
looksLikeSafeString(argument) &&
227-
(
228-
argument.indexOf('https://') === 0 ||
229-
argument.indexOf('http://') === 0
230-
)
231-
)
86+
function displayPackage (entry) {
87+
return entry.name + '@' + entry.version + ': ' + entry.support
23288
}

‎lib/utils/valid-support-url.js

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
const URL = require('url').URL
2+
3+
// Is the value of a `support` property of a `package.json` object
4+
// a valid URL for `npm support` to display?
5+
module.exports = function (argument) {
6+
if (typeof argument !== 'string' || argument.length === 0) {
7+
return false
8+
}
9+
try {
10+
var parsed = new URL(argument)
11+
} catch (error) {
12+
return false
13+
}
14+
if (
15+
parsed.protocol !== 'https:' &&
16+
parsed.protocol !== 'http:'
17+
) return false
18+
return parsed.host
19+
}

‎package-lock.json

+2-44
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)