Rush StackShopBlogEvents
Skip to main content

Preferred versions

Background

Rush performs a single install for all your projects by creating a fake rush-common project in your common folder that references tarballs containing the dependencies for each project. For example, suppose your rush.json has two projects "project1" and "project2". The generated file might look like this:

common/temp/package.json

{
"name": "rush-common",
"description": "Temporary file generated by the Rush tool",
"private": true,
"version": "0.0.0",
"dependencies": {
"@rush-temp/project1": "file:./projects/project-1.tgz",
"@rush-temp/project2": "file:./projects/project-2.tgz"
}
}

The package manager considers each of these "@rush-temp" scoped projects to be a direct dependency for the rush-common project. In general NPM installs a project's direct dependencies first (at the root of the node_modules tree), and only then proceed to download the indirect dependencies. But since your real project's direct dependencies are now indirect dependencies for the rush-common project, the npm install behavior could be different.

Suppose project-1/package.json looks like this:

{
"name": "project-1",
"version": "1.0.0",
"dependencies": {
"library-a": "1.0.1",
"library-b": "1.1.3"
}
}

And let's say library-a (from the internet) looks like this:

{
"name": "library-a",
"version": "1.0.1",
"dependencies": {
"library-b": "^1.0.0"
}
}

If you ran a normal npm install in for project-1, you would expect to have a node_modules folder which looks like this, even if library-b@1.4.4 exists in the NPM registry:

node_modules/
library-a/ (1.0.1)
library-b/ (1.1.3)

Even though library-b@1.4.4 matches the "^1.0.0" SemVer pattern, NPM doesn't install it because 1.1.3 (installed by project-1) already satisfies it.

But the common/temp/package.json described above would not guarantee this. Instead, depending on the dependencies of project-2, you could end up with this:

node_modules/
project-1/
library-b/ (1.1.3)
library-a/ (1.0.1)
library-b/ (1.4.4)

... which is also a valid solution to the SemVer equation. Similar problems can arise when using Rush with NPM's peer dependencies.

Preferred Versions

To control these effects Rush introduces a concept of "preferred versions", which are dependencies that get explicitly added to the top-level common/temp/package.json.

You can "pin" a version by adding it to the config file common-versions.json. For example:

common/config/rush/common-versions.json

{
"$schema": "https://developer.microsoft.com/json-schemas/rush/v5/common-versions.schema.json",
"preferredVersions": {
"css-loader": "1.2.3"
}
}

This will cause css-loader to be added to the common/temp/package.json from our above example, like this:

{
"name": "rush-common",
"description": "Temporary file generated by the Rush tool",
"private": true,
"version": "0.0.0",
"dependencies": {
"css-loader": "1.2.3",
"@rush-temp/project1": "file:./projects/project-1.tgz",
"@rush-temp/project2": "file:./projects/project-2.tgz"
}
}

Note: If you are publishing packages, you should be careful about adding preferred versions in a way that would produce a different result than a person who installs your library normally using NPM.

Implicitly Preferred Versions

By default, Rush will automatically add all direct dependencies of all your projects to common/temp/package.json. In our original example, these "implicitly preferred versions" might appear like this:

common/temp/package.json

{
"name": "rush-common",
"description": "Temporary file generated by the Rush tool",
"private": true,
"version": "0.0.0",
"dependencies": {
"css-loader": "1.2.3", // <---- preferred version that was specified above
"library-a": "~1.0.0", // <---- implicitly preferred version
"library-b": "1.1.3", // <---- implicitly preferred version
"@rush-temp/project1": "file:./projects/project-1.tgz",
"@rush-temp/project2": "file:./projects/project-2.tgz",
}
}

Rush does this for you except in cases where different projects specify different version ranges for a given dependency. In that case, Rush wouldn't know which version should be implicitly preferred. For example, if project1 and project2 were requesting different versions of library-b, then you might need to get involved, and maybe use common-versions.json to resolve your issue.

For older package managers, automatically adding these entries tended to reduce duplication of indirect dependencies. However, implicitly preferred versions can cause trouble for certain dependencies with incompatible peerDependencies ranges. If you're encountering installation errors involving peer dependencies, we recommend to disable this behavior by setting implicitlyPreferredVersions to false in the common/config/rush/common-versions.json config file.