/
githubmirror
/
yarn
Обзор
Документация
Войти
/
githubmirror
/
yarn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/cli/commands/upgrade.js
240 строк
9 KB
David Sanders
fix(add, upgrade): fix audit flag (#7326)
13 июн 2019, 18:07
13 июн 2019, 18:07
40546bf
Код
Авторство
О чём код?
/* @flow */ import type {Dependency} from '../../types.js'; import type {Reporter} from '../../reporters/index.js'; import type Config from '../../config.js'; import type {DependencyRequestPatterns} from '../../types.js'; import {Add} from './add.js'; import Lockfile from '../../lockfile'; import PackageRequest from '../../package-request.js'; import {normalizePattern} from '../../util/normalize-pattern.js'; import {Install} from './install.js'; // used to detect whether a semver range is simple enough to preserve when doing a --latest upgrade. // when not matched, the upgraded version range will default to `^` the same as the `add` command would. const basicSemverOperatorRegex = new RegExp('^(\\^|~|>=|<=)?[^ |&,]+$'); // used to detect if a passed parameter is a scope or a package name. const validScopeRegex = /^@[a-zA-Z0-9-][a-zA-Z0-9_.-]*\/$/; // If specific versions were requested for packages, override what getOutdated reported as the latest to install // Also add ones that are missing, since the requested packages may not have been outdated at all. function setUserRequestedPackageVersions( deps: Array<Dependency>, args: Array<string>, latest: boolean, packagePatterns, reporter: Reporter, ) { args.forEach(requestedPattern => { let found = false; let normalized = normalizePattern(requestedPattern); // if the user specified a package name without a version range, then that implies "latest" // but if the latest flag is not passed then we need to use the version range from package.json if (!normalized.hasVersion && !latest) { packagePatterns.forEach(packagePattern => { const packageNormalized = normalizePattern(packagePattern.pattern); if (packageNormalized.name === normalized.name) { normalized = packageNormalized; } }); } const newPattern = `${normalized.name}@${normalized.range}`; // if this dependency is already in the outdated list, // just update the upgradeTo to whatever version the user requested. deps.forEach(dep => { if (normalized.hasVersion && dep.name === normalized.name) { found = true; dep.upgradeTo = newPattern; reporter.verbose(reporter.lang('verboseUpgradeBecauseRequested', requestedPattern, newPattern)); } }); // if this dependency was not in the outdated list, // then add a new entry if (normalized.hasVersion && !found) { deps.push({ name: normalized.name, wanted: '', latest: '', url: '', hint: '', range: '', current: '', upgradeTo: newPattern, workspaceName: '', workspaceLoc: '', }); reporter.verbose(reporter.lang('verboseUpgradeBecauseRequested', requestedPattern, newPattern)); } }); } // this function attempts to determine the range operator on the semver range. // this will only handle the simple cases of a semver starting with '^', '~', '>=', '<=', or an exact version. // "exotic" semver ranges will not be handled. function getRangeOperator(version): string { const result = basicSemverOperatorRegex.exec(version); return result ? result[1] || '' : '^'; } // Attempt to preserve the range operator from the package.json specified semver range. // If an explicit operator was specified using --exact, --tilde, --caret, then that will take precedence. function buildPatternToUpgradeTo(dep, flags): string { if (dep.latest === 'exotic') { return `${dep.name}@${dep.url}`; } const toLatest = flags.latest; const toVersion = toLatest ? dep.latest : dep.range; let rangeOperator = ''; if (toLatest) { if (flags.caret) { rangeOperator = '^'; } else if (flags.tilde) { rangeOperator = '~'; } else if (flags.exact) { rangeOperator = ''; } else { rangeOperator = getRangeOperator(dep.range); } } return `${dep.name}@${rangeOperator}${toVersion}`; } function scopeFilter(flags: Object, dep: Dependency): boolean { if (validScopeRegex.test(flags.scope)) { return dep.name.startsWith(flags.scope); } return true; } // Remove deps being upgraded from the lockfile, or else Add will use the already-installed version // instead of the latest for the range. // We do this recursively so that when Yarn installs the potentially updated transitive deps, // it may upgrade them too instead of just using the "locked" version from the lockfile. // Transitive dependencies that are also a direct dependency are skipped. export function cleanLockfile( lockfile: Lockfile, deps: Array<Dependency>, packagePatterns: DependencyRequestPatterns, reporter: Reporter, ) { function cleanDepFromLockfile(pattern: string, depth: number) { const lockManifest = lockfile.getLocked(pattern); if (!lockManifest || (depth > 1 && packagePatterns.some(packagePattern => packagePattern.pattern === pattern))) { reporter.verbose(reporter.lang('verboseUpgradeNotUnlocking', pattern)); return; } const dependencies = Object.assign({}, lockManifest.dependencies || {}, lockManifest.optionalDependencies || {}); const depPatterns = Object.keys(dependencies).map(key => `${key}@${dependencies[key]}`); reporter.verbose(reporter.lang('verboseUpgradeUnlocking', pattern)); lockfile.removePattern(pattern); depPatterns.forEach(pattern => cleanDepFromLockfile(pattern, depth + 1)); } const patterns = deps.map(dep => dep.upgradeTo); patterns.forEach(pattern => cleanDepFromLockfile(pattern, 1)); } export function setFlags(commander: Object) { commander.description('Upgrades packages to their latest version based on the specified range.'); commander.usage('upgrade [flags]'); commander.option('-S, --scope <scope>', 'upgrade packages under the specified scope'); commander.option('-L, --latest', 'list the latest version of packages, ignoring version ranges in package.json'); commander.option('-E, --exact', 'install exact version. Only used when --latest is specified.'); commander.option('-P, --pattern [pattern]', 'upgrade packages that match pattern'); commander.option( '-T, --tilde', 'install most recent release with the same minor version. Only used when --latest is specified.', ); commander.option( '-C, --caret', 'install most recent release with the same major version. Only used when --latest is specified.', ); commander.option('-A, --audit', 'Run vulnerability audit on installed packages'); } export function hasWrapper(commander: Object, args: Array<string>): boolean { return true; } export const requireLockfile = true; export async function run(config: Config, reporter: Reporter, flags: Object, args: Array<string>): Promise<void> { let addArgs = []; const upgradeAll = args.length === 0 && typeof flags.scope === 'undefined' && typeof flags.pattern === 'undefined'; const addFlags = Object.assign({}, flags, { force: true, ignoreWorkspaceRootCheck: true, workspaceRootIsCwd: config.cwd === config.lockfileFolder, }); const lockfile = await Lockfile.fromDirectory(config.lockfileFolder, reporter); const deps = await getOutdated(config, reporter, flags, lockfile, args); const install = new Install(flags, config, reporter, lockfile); const {requests: packagePatterns} = await install.fetchRequestFromCwd(); setUserRequestedPackageVersions(deps, args, flags.latest, packagePatterns, reporter); cleanLockfile(lockfile, deps, packagePatterns, reporter); addArgs = deps.map(dep => dep.upgradeTo); if (flags.scope && validScopeRegex.test(flags.scope)) { addArgs = addArgs.filter(depName => depName.startsWith(flags.scope)); } const add = new Add(addArgs, addFlags, config, reporter, upgradeAll ? new Lockfile() : lockfile); await add.init(); } export async function getOutdated( config: Config, reporter: Reporter, flags: Object, lockfile: Lockfile, patterns: Array<string>, ): Promise<Array<Dependency>> { const install = new Install(flags, config, reporter, lockfile); const outdatedFieldName = flags.latest ? 'latest' : 'wanted'; // ensure scope is of the form `@scope/` const normalizeScope = function() { if (flags.scope) { if (!flags.scope.startsWith('@')) { flags.scope = '@' + flags.scope; } if (!flags.scope.endsWith('/')) { flags.scope += '/'; } } }; const versionFilter = function(dep: Dependency): boolean { return dep.current !== dep[outdatedFieldName]; }; if (!flags.latest) { // these flags only have an affect when --latest is used flags.tilde = false; flags.exact = false; flags.caret = false; } normalizeScope(); const deps = (await PackageRequest.getOutdatedPackages(lockfile, install, config, reporter, patterns, flags)) .filter(versionFilter) .filter(scopeFilter.bind(this, flags)); deps.forEach(dep => { dep.upgradeTo = buildPatternToUpgradeTo(dep, flags); reporter.verbose(reporter.lang('verboseUpgradeBecauseOutdated', dep.name, dep.upgradeTo)); }); return deps; }