in apps/rush-lib/src/logic/PackageJsonUpdater.ts [296:499]
private async _getNormalizedVersionSpec(
projects: RushConfigurationProject[],
installManager: BaseInstallManager,
packageName: string,
initialSpec: string | undefined,
implicitlyPinnedVersion: string | undefined,
rangeStyle: SemVerStyle
): Promise<string> {
console.log(colors.gray(`Determining new version for dependency: ${packageName}`));
if (initialSpec) {
console.log(`Specified version selector: ${colors.cyan(initialSpec)}`);
} else {
console.log(`No version selector was specified, so the version will be determined automatically.`);
}
console.log();
// determine if the package is a project in the local repository and if the version exists
const localProject: RushConfigurationProject | undefined = this._tryGetLocalProject(
packageName,
projects
);
// if ensureConsistentVersions => reuse the pinned version
// else, query the registry and use the latest that satisfies semver spec
if (initialSpec && implicitlyPinnedVersion && initialSpec === implicitlyPinnedVersion) {
console.log(
colors.green('Assigning "') +
colors.cyan(initialSpec) +
colors.green(
`" for "${packageName}" because it matches what other projects are using in this repo.`
)
);
return initialSpec;
}
if (this._rushConfiguration.ensureConsistentVersions && !initialSpec && implicitlyPinnedVersion) {
console.log(
`Assigning the version range "${colors.cyan(implicitlyPinnedVersion)}" for "${packageName}" because` +
` it is already used by other projects in this repo.`
);
return implicitlyPinnedVersion;
}
await InstallHelpers.ensureLocalPackageManager(
this._rushConfiguration,
this._rushGlobalFolder,
RushConstants.defaultMaxInstallAttempts
);
const useWorkspaces: boolean = !!(
this._rushConfiguration.pnpmOptions && this._rushConfiguration.pnpmOptions.useWorkspaces
);
const workspacePrefix: string = 'workspace:';
// Trim 'workspace:' notation from the spec, since we're going to be tweaking the range
if (useWorkspaces && initialSpec && initialSpec.startsWith(workspacePrefix)) {
initialSpec = initialSpec.substring(workspacePrefix.length).trim();
}
let selectedVersion: string | undefined;
let selectedVersionPrefix: string = '';
if (initialSpec && initialSpec !== 'latest') {
console.log(colors.gray('Finding versions that satisfy the selector: ') + initialSpec);
console.log();
if (localProject !== undefined) {
const version: string = localProject.packageJson.version;
if (semver.satisfies(version, initialSpec)) {
// For workspaces, assume that specifying the exact version means you always want to consume
// the local project. Otherwise, use the exact local package version
if (useWorkspaces) {
selectedVersion = initialSpec === version ? '*' : initialSpec;
selectedVersionPrefix = workspacePrefix;
} else {
selectedVersion = version;
}
} else {
throw new Error(
`The dependency being added ("${packageName}") is a project in the local Rush repository, ` +
`but the version specifier provided (${initialSpec}) does not match the local project's version ` +
`(${version}). Correct the version specifier, omit a version specifier, or include "${packageName}" as a ` +
`cyclicDependencyProject if it is intended for "${packageName}" to come from an external feed and not ` +
'from the local Rush repository.'
);
}
} else {
console.log(`Querying registry for all versions of "${packageName}"...`);
let commandArgs: string[];
if (this._rushConfiguration.packageManager === 'yarn') {
commandArgs = ['info', packageName, 'versions', '--json'];
} else {
commandArgs = ['view', packageName, 'versions', '--json'];
}
const allVersions: string = Utilities.executeCommandAndCaptureOutput(
this._rushConfiguration.packageManagerToolFilename,
commandArgs,
this._rushConfiguration.commonTempFolder
);
let versionList: string[];
if (this._rushConfiguration.packageManager === 'yarn') {
versionList = JSON.parse(allVersions).data;
} else {
versionList = JSON.parse(allVersions);
}
console.log(colors.gray(`Found ${versionList.length} available versions.`));
for (const version of versionList) {
if (semver.satisfies(version, initialSpec)) {
selectedVersion = initialSpec;
console.log(`Found a version that satisfies ${initialSpec}: ${colors.cyan(version)}`);
break;
}
}
if (!selectedVersion) {
throw new Error(
`Unable to find a version of "${packageName}" that satisfies` +
` the version specifier "${initialSpec}"`
);
}
}
} else {
if (!this._rushConfiguration.ensureConsistentVersions) {
console.log(
colors.gray(
`The "ensureConsistentVersions" policy is NOT active, so we will assign the latest version.`
)
);
console.log();
}
if (localProject !== undefined) {
// For workspaces, assume that no specified version range means you always want to consume
// the local project. Otherwise, use the exact local package version
if (useWorkspaces) {
selectedVersion = '*';
selectedVersionPrefix = workspacePrefix;
} else {
selectedVersion = localProject.packageJson.version;
}
} else {
console.log(`Querying NPM registry for latest version of "${packageName}"...`);
let commandArgs: string[];
if (this._rushConfiguration.packageManager === 'yarn') {
commandArgs = ['info', packageName, 'dist-tags.latest', '--silent'];
} else {
commandArgs = ['view', `${packageName}@latest`, 'version'];
}
selectedVersion = Utilities.executeCommandAndCaptureOutput(
this._rushConfiguration.packageManagerToolFilename,
commandArgs,
this._rushConfiguration.commonTempFolder
).trim();
}
console.log();
console.log(`Found latest version: ${colors.cyan(selectedVersion!)}`);
}
console.log();
let reasonForModification: string = '';
if (selectedVersion !== '*') {
switch (rangeStyle) {
case SemVerStyle.Caret: {
selectedVersionPrefix += '^';
reasonForModification = ' because the "--caret" flag was specified';
break;
}
case SemVerStyle.Exact: {
reasonForModification = ' because the "--exact" flag was specified';
break;
}
case SemVerStyle.Tilde: {
selectedVersionPrefix += '~';
break;
}
case SemVerStyle.Passthrough: {
break;
}
default: {
throw new Error(`Unexpected SemVerStyle ${rangeStyle}.`);
}
}
}
const normalizedVersion: string = selectedVersionPrefix + selectedVersion;
console.log(
colors.gray(`Assigning version "${normalizedVersion}" for "${packageName}"${reasonForModification}.`)
);
return normalizedVersion;
}