@dependabot/yarn-lib
Version:
📦🐈 Fast, reliable, and secure dependency management.
488 lines (385 loc) • 16.7 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.SCOPE_SEPARATOR = undefined;
var _asyncToGenerator2;
function _load_asyncToGenerator() {
return _asyncToGenerator2 = _interopRequireDefault(require('babel-runtime/helpers/asyncToGenerator'));
}
var _constants;
function _load_constants() {
return _constants = require('../constants.js');
}
var _fs;
function _load_fs() {
return _fs = _interopRequireWildcard(require('../util/fs.js'));
}
var _npmResolver;
function _load_npmResolver() {
return _npmResolver = _interopRequireDefault(require('../resolvers/registries/npm-resolver.js'));
}
var _envReplace;
function _load_envReplace() {
return _envReplace = _interopRequireDefault(require('../util/env-replace.js'));
}
var _baseRegistry;
function _load_baseRegistry() {
return _baseRegistry = _interopRequireDefault(require('./base-registry.js'));
}
var _misc;
function _load_misc() {
return _misc = require('../util/misc');
}
var _path;
function _load_path() {
return _path = require('../util/path');
}
var _normalizeUrl;
function _load_normalizeUrl() {
return _normalizeUrl = _interopRequireDefault(require('normalize-url'));
}
var _userHomeDir;
function _load_userHomeDir() {
return _userHomeDir = _interopRequireDefault(require('../util/user-home-dir'));
}
var _userHomeDir2;
function _load_userHomeDir2() {
return _userHomeDir2 = require('../util/user-home-dir');
}
var _path2;
function _load_path2() {
return _path2 = _interopRequireDefault(require('path'));
}
var _url;
function _load_url() {
return _url = _interopRequireDefault(require('url'));
}
var _ini;
function _load_ini() {
return _ini = _interopRequireDefault(require('ini'));
}
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const DEFAULT_REGISTRY = 'https://registry.npmjs.org/';
const REGEX_REGISTRY_HTTP_PROTOCOL = /^https?:/i;
const REGEX_REGISTRY_PREFIX = /^(https?:)?\/\//i;
const REGEX_REGISTRY_SUFFIX = /registry\/?$/;
const SCOPE_SEPARATOR = exports.SCOPE_SEPARATOR = '%2f';
// All scoped package names are of the format `@scope%2fpkg` from the use of NpmRegistry.escapeName
// `(?:^|\/)` Match either the start of the string or a `/` but don't capture
// `[^\/?]+?` Match any character that is not '/' or '?' and capture, up until the first occurance of:
// `(?=%2f|\/)` Match SCOPE_SEPARATOR, the escaped '/', or a raw `/` and don't capture
// The reason for matching a plain `/` is NPM registry being inconsistent about escaping `/` in
// scoped package names: when you're fetching a tarball, it is not escaped, when you want info
// about the package, it is escaped.
const SCOPED_PKG_REGEXP = /(?:^|\/)(@[^\/?]+?)(?=%2f|\/)/;
// TODO: Use the method from src/cli/commands/global.js for this instead
function getGlobalPrefix() {
if (process.env.PREFIX) {
return process.env.PREFIX;
} else if (process.platform === 'win32') {
// c:\node\node.exe --> prefix=c:\node\
return (_path2 || _load_path2()).default.dirname(process.execPath);
} else {
// /usr/local/bin/node --> prefix=/usr/local
let prefix = (_path2 || _load_path2()).default.dirname((_path2 || _load_path2()).default.dirname(process.execPath));
// destdir only is respected on Unix
if (process.env.DESTDIR) {
prefix = (_path2 || _load_path2()).default.join(process.env.DESTDIR, prefix);
}
return prefix;
}
}
const PATH_CONFIG_OPTIONS = new Set(['cache', 'cafile', 'prefix', 'userconfig']);
function isPathConfigOption(key) {
return PATH_CONFIG_OPTIONS.has(key);
}
function normalizePath(val) {
if (val === undefined) {
return undefined;
}
if (typeof val !== 'string') {
val = String(val);
}
return (0, (_path || _load_path()).resolveWithHome)(val);
}
function urlParts(requestUrl) {
const normalizedUrl = (0, (_normalizeUrl || _load_normalizeUrl()).default)(requestUrl);
const parsed = (_url || _load_url()).default.parse(normalizedUrl);
const host = parsed.host || '';
const path = parsed.path || '';
return { host, path };
}
class NpmRegistry extends (_baseRegistry || _load_baseRegistry()).default {
constructor(cwd, registries, requestManager, reporter) {
super(cwd, registries, requestManager, reporter);
this.folder = 'node_modules';
}
static escapeName(name) {
// scoped packages contain slashes and the npm registry expects them to be escaped
return name.replace('/', SCOPE_SEPARATOR);
}
isScopedPackage(packageIdent) {
return SCOPED_PKG_REGEXP.test(packageIdent);
}
getRequestUrl(registry, pathname) {
const isUrl = REGEX_REGISTRY_PREFIX.test(pathname);
if (isUrl) {
return pathname;
} else {
return (_url || _load_url()).default.resolve((0, (_misc || _load_misc()).addSuffix)(registry, '/'), pathname);
}
}
isRequestToRegistry(requestUrl, registryUrl) {
const request = urlParts(requestUrl);
const registry = urlParts(registryUrl);
const customHostSuffix = this.getRegistryOrGlobalOption(registryUrl, 'custom-host-suffix');
const requestToRegistryHost = request.host === registry.host;
const requestToYarn = (_constants || _load_constants()).YARN_REGISTRY.includes(request.host) && DEFAULT_REGISTRY.includes(registry.host);
const requestToRegistryPath = request.path.startsWith(registry.path);
// For some registries, the package path does not prefix with the registry path
const customHostSuffixInUse = typeof customHostSuffix === 'string' && request.host.endsWith(customHostSuffix);
return (requestToRegistryHost || requestToYarn) && (requestToRegistryPath || customHostSuffixInUse);
}
request(pathname, opts = {}, packageName) {
// packageName needs to be escaped when if it is passed
const packageIdent = packageName && NpmRegistry.escapeName(packageName) || pathname;
const registry = this.getRegistry(packageIdent);
const requestUrl = this.getRequestUrl(registry, pathname);
const alwaysAuth = this.getRegistryOrGlobalOption(registry, 'always-auth');
const headers = Object.assign({
Accept: 'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*'
}, opts.headers);
const isToRegistry = this.isRequestToRegistry(requestUrl, registry) || this.requestNeedsAuth(requestUrl);
// this.token must be checked to account for publish requests on non-scopped packages
if (this.token || isToRegistry && (alwaysAuth || this.isScopedPackage(packageIdent))) {
const authorization = this.getAuth(packageIdent);
if (authorization) {
headers.authorization = authorization;
}
}
return this.requestManager.request({
url: requestUrl,
method: opts.method,
body: opts.body,
auth: opts.auth,
headers,
json: !opts.buffer,
buffer: opts.buffer,
process: opts.process,
gzip: true
});
}
requestNeedsAuth(requestUrl) {
const config = this.config;
const requestParts = urlParts(requestUrl);
return !!Object.keys(config).find(option => {
const parts = option.split(':');
if (parts.length === 2 && parts[1] === '_authToken') {
const registryParts = urlParts(parts[0]);
if (requestParts.host === registryParts.host && requestParts.path.startsWith(registryParts.path)) {
return true;
}
}
return false;
});
}
checkOutdated(config, name, range) {
var _this = this;
return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () {
const req = yield _this.request(NpmRegistry.escapeName(name), {
headers: { Accept: 'application/json' }
});
if (!req) {
throw new Error('couldnt find ' + name);
}
// By default use top level 'repository' and 'homepage' values
let repository = req.repository,
homepage = req.homepage;
const wantedPkg = yield (_npmResolver || _load_npmResolver()).default.findVersionInRegistryResponse(config, range, req);
// But some local repositories like Verdaccio do not return 'repository' nor 'homepage'
// in top level data structure, so we fallback to wanted package manifest
if (!repository && !homepage) {
repository = wantedPkg.repository;
homepage = wantedPkg.homepage;
}
const url = homepage || repository && repository.url || '';
return {
latest: req['dist-tags'].latest,
wanted: wantedPkg.version,
url
};
})();
}
getPossibleConfigLocations(filename, reporter) {
var _this2 = this;
return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () {
// npmrc --> ./.npmrc, ~/.npmrc, ${prefix}/etc/npmrc
const localfile = '.' + filename;
const possibles = [[false, (_path2 || _load_path2()).default.join(_this2.cwd, localfile)], [true, _this2.config.userconfig || (_path2 || _load_path2()).default.join((_userHomeDir || _load_userHomeDir()).default, localfile)], [false, (_path2 || _load_path2()).default.join(getGlobalPrefix(), 'etc', filename)]];
// When home directory for global install is different from where $HOME/npmrc is stored,
// E.g. /usr/local/share vs /root on linux machines, check the additional location
if ((_userHomeDir2 || _load_userHomeDir2()).home !== (_userHomeDir || _load_userHomeDir()).default) {
possibles.push([true, (_path2 || _load_path2()).default.join((_userHomeDir2 || _load_userHomeDir2()).home, localfile)]);
}
// npmrc --> ../.npmrc, ../../.npmrc, etc.
const foldersFromRootToCwd = (0, (_path || _load_path()).getPosixPath)(_this2.cwd).split('/');
while (foldersFromRootToCwd.length > 1) {
possibles.push([false, (_path2 || _load_path2()).default.join(foldersFromRootToCwd.join((_path2 || _load_path2()).default.sep), localfile)]);
foldersFromRootToCwd.pop();
}
const actuals = [];
for (var _iterator = possibles, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
var _ref2;
if (_isArray) {
if (_i >= _iterator.length) break;
_ref2 = _iterator[_i++];
} else {
_i = _iterator.next();
if (_i.done) break;
_ref2 = _i.value;
}
const _ref = _ref2;
const isHome = _ref[0];
const loc = _ref[1];
reporter.verbose(reporter.lang('configPossibleFile', loc));
if (yield (_fs || _load_fs()).exists(loc)) {
reporter.verbose(reporter.lang('configFileFound', loc));
actuals.push([isHome, loc, yield (_fs || _load_fs()).readFile(loc)]);
}
}
return actuals;
})();
}
static getConfigEnv(env = process.env) {
// To match NPM's behavior, HOME is always the user's home directory.
const overrideEnv = {
HOME: (_userHomeDir2 || _load_userHomeDir2()).home
};
return Object.assign({}, env, overrideEnv);
}
static normalizeConfig(config) {
const env = NpmRegistry.getConfigEnv();
config = (_baseRegistry || _load_baseRegistry()).default.normalizeConfig(config);
for (const key in config) {
config[key] = (0, (_envReplace || _load_envReplace()).default)(config[key], env);
if (isPathConfigOption(key)) {
config[key] = normalizePath(config[key]);
}
}
return config;
}
loadConfig() {
var _this3 = this;
return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () {
// docs: https://docs.npmjs.com/misc/config
_this3.mergeEnv('npm_config_');
for (var _iterator2 = yield _this3.getPossibleConfigLocations('npmrc', _this3.reporter), _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) {
var _ref4;
if (_isArray2) {
if (_i2 >= _iterator2.length) break;
_ref4 = _iterator2[_i2++];
} else {
_i2 = _iterator2.next();
if (_i2.done) break;
_ref4 = _i2.value;
}
const _ref3 = _ref4;
const loc = _ref3[1];
const file = _ref3[2];
const config = NpmRegistry.normalizeConfig((_ini || _load_ini()).default.parse(file));
// normalize offline mirror path relative to the current npmrc
const offlineLoc = config['yarn-offline-mirror'];
// don't normalize if we already have a mirror path
if (!_this3.config['yarn-offline-mirror'] && offlineLoc) {
const mirrorLoc = config['yarn-offline-mirror'] = (_path2 || _load_path2()).default.resolve((_path2 || _load_path2()).default.dirname(loc), offlineLoc);
yield (_fs || _load_fs()).mkdirp(mirrorLoc);
}
_this3.config = Object.assign({}, config, _this3.config);
}
})();
}
getScope(packageIdent) {
const match = packageIdent.match(SCOPED_PKG_REGEXP);
return match && match[1] || '';
}
getRegistry(packageIdent) {
// Try extracting registry from the url, then scoped registry, and default registry
if (packageIdent.match(REGEX_REGISTRY_PREFIX)) {
const availableRegistries = this.getAvailableRegistries();
const registry = availableRegistries.find(registry => packageIdent.startsWith(registry));
if (registry) {
return String(registry);
}
}
var _arr = [this.getScope(packageIdent), ''];
for (var _i3 = 0; _i3 < _arr.length; _i3++) {
const scope = _arr[_i3];
const registry = this.getScopedOption(scope, 'registry') || this.registries.yarn.getScopedOption(scope, 'registry');
if (registry) {
return String(registry);
}
}
return DEFAULT_REGISTRY;
}
getAuth(packageIdent) {
if (this.token) {
return this.token;
}
const baseRegistry = this.getRegistry(packageIdent);
const registries = [baseRegistry];
// If sending a request to the Yarn registry, we must also send it the auth token for the npm registry
if (baseRegistry === (_constants || _load_constants()).YARN_REGISTRY) {
registries.push(DEFAULT_REGISTRY);
}
for (var _iterator3 = registries, _isArray3 = Array.isArray(_iterator3), _i4 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) {
var _ref5;
if (_isArray3) {
if (_i4 >= _iterator3.length) break;
_ref5 = _iterator3[_i4++];
} else {
_i4 = _iterator3.next();
if (_i4.done) break;
_ref5 = _i4.value;
}
const registry = _ref5;
// Check for bearer token.
const authToken = this.getRegistryOrGlobalOption(registry, '_authToken');
if (authToken) {
return `Bearer ${String(authToken)}`;
}
// Check for basic auth token.
const auth = this.getRegistryOrGlobalOption(registry, '_auth');
if (auth) {
return `Basic ${String(auth)}`;
}
// Check for basic username/password auth.
const username = this.getRegistryOrGlobalOption(registry, 'username');
const password = this.getRegistryOrGlobalOption(registry, '_password');
if (username && password) {
const pw = new Buffer(String(password), 'base64').toString();
return 'Basic ' + new Buffer(String(username) + ':' + pw).toString('base64');
}
}
return '';
}
getScopedOption(scope, option) {
return this.getOption(scope + (scope ? ':' : '') + option);
}
getRegistryOption(registry, option) {
const pre = REGEX_REGISTRY_HTTP_PROTOCOL;
const suf = REGEX_REGISTRY_SUFFIX;
// When registry is used config scope, the trailing '/' is required
const reg = (0, (_misc || _load_misc()).addSuffix)(registry, '/');
// 1st attempt, try to get option for the given registry URL
// 2nd attempt, remove the 'https?:' prefix of the registry URL
// 3nd attempt, remove the 'registry/?' suffix of the registry URL
return this.getScopedOption(reg, option) || pre.test(reg) && this.getRegistryOption(reg.replace(pre, ''), option) || suf.test(reg) && this.getRegistryOption(reg.replace(suf, ''), option);
}
getRegistryOrGlobalOption(registry, option) {
return this.getRegistryOption(registry, option) || this.getOption(option);
}
}
exports.default = NpmRegistry;
NpmRegistry.filename = 'package.json';