-
-
Notifications
You must be signed in to change notification settings - Fork 27
/
Copy pathindex.js
98 lines (77 loc) · 2.82 KB
/
index.js
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
'use strict';
const isUrl = require('is-url-superb');
const got = require('got');
const isScoped = require('is-scoped');
const configuredRegistryUrl = require('registry-url')();
const registryAuthToken = require('registry-auth-token');
const zip = require('lodash.zip');
const validate = require('validate-npm-package-name');
const organizationRegex = require('org-regex')({exact: true});
const pMap = require('p-map');
class InvalidNameError extends Error {}
const npmOrganizationUrl = 'https://www.npmjs.com/org/';
const request = async (name, options) => {
const registryUrl = normalizeUrl(options.registryUrl || configuredRegistryUrl);
const isOrganization = organizationRegex.test(name);
if (isOrganization) {
name = name.replace(/[@/]/g, '');
}
const isValid = validate(name);
if (!isValid.validForNewPackages) {
const notices = [...isValid.warnings || [], ...isValid.errors || []].map(v => `- ${v}`);
notices.unshift(`Invalid package name: ${name}`);
const error = new InvalidNameError(notices.join('\n'));
error.warnings = isValid.warnings;
error.errors = isValid.errors;
throw error;
}
const isScopedPackage = isScoped(name);
if (isScopedPackage) {
name = name.replace(/\//g, '%2f');
}
const authInfo = registryAuthToken(registryUrl, {recursive: true});
const headers = {};
if (authInfo) {
headers.authorization = `${authInfo.type} ${authInfo.token}`;
}
try {
if (isOrganization) {
await got.head(npmOrganizationUrl + name.toLowerCase(), {timeout: 10000});
} else {
await got.head(registryUrl + name.toLowerCase(), {timeout: 10000, headers});
}
return false;
} catch (error) {
const {statusCode} = error.response;
if (statusCode === 404) {
return true;
}
if (isScopedPackage && statusCode === 401) {
return true;
}
throw error;
}
};
// Ensure the URL always ends in a `/`
const normalizeUrl = url => url.replace(/\/$/, '') + '/';
const npmName = async (name, options = {}) => {
if (!(typeof name === 'string' && name.length > 0)) {
throw new Error('Package name required');
}
if (typeof options.registryUrl !== 'undefined' && !(typeof options.registryUrl === 'string' && isUrl(options.registryUrl))) {
throw new Error('The `registryUrl` option must be a valid string URL');
}
return request(name, options);
};
module.exports = npmName;
module.exports.many = async (names, options = {}) => {
if (!Array.isArray(names)) {
throw new TypeError(`Expected an array of names, got ${typeof names}`);
}
if (typeof options.registryUrl !== 'undefined' && !(typeof options.registryUrl === 'string' && isUrl(options.registryUrl))) {
throw new Error('The `registryUrl` option must be a valid string URL');
}
const result = await pMap(names, name => request(name, options), {stopOnError: false});
return new Map(zip(names, result));
};
module.exports.InvalidNameError = InvalidNameError;