UNPKG

@dependabot/yarn-lib

Version:

📦🐈 Fast, reliable, and secure dependency management.

488 lines (385 loc) 16.7 kB
'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';