Rsdoctor will report cases where multiple duplicate dependencies exist in the same builder's artifact.
To solve the issue of multiple versions of dependencies, you can address it from both the dependency and build aspects.
Generally, package managers try to install the same version of a package based on the semver range. However, long-term projects may have some duplicate dependencies due to the existence of lock files.
Package managers provide the dedupe
command, such as npm/yarn/pnpm dedupe
, to optimize duplicate dependencies within the correct semver range.
Under the constraints of semver, the effectiveness of the dedupe command may not be ideal. For example, if the artifact contains dependencies debug@4.3.4
and debug@3.0.0
, where they are respectively depended on by "debug": "^4"
and another package's "debug": "^3"
.
In this case, you can try using the resolutions
feature of the package manager, such as pnpm's pnpm.overrides, .pnpmfile.cjs, or yarn's resolutions.
The advantage of these features is that they can break free from the constraints of semver and change the version declared in package.json
during installation to precisely control the installed version.
However, before using them, it is important to consider the compatibility between package versions and evaluate whether optimization is necessary. For example, whether the logic changes between different versions of the same package will affect the functionality of the project.
Almost all builders support modifying the paths for resolving npm packages. Therefore, we can eliminate duplicate dependencies by manually specifying the resolve paths for packages during compilation. For example, using Rspack or Webpack, if lodash
is duplicated in the build, we can configure it as follows to specify the resolve paths for all lodash
packages to the node_modules
directory in the current directory.
This method also requires attention to the compatibility between package versions.
In this project, the web
app depends on react@18.2.0
and imports component
using "component": "workspace:*"
. The component
package, in turn, depends on react@18.1.0
. The project structure is as follows:
When executing webpack build
under apps/web
, the code in the web
directory will be resolved to react@18.2.0
, and then the code in the component
directory will be resolved to react@18.1.0
. This results in the output of the web project containing two versions of React
.
This issue can be resolved using the resolve.alias
configuration in the builder, such as Rspack
or Webpack
. By specifying the resolve path for React
to only resolve to apps/web/node_modules/react
, you can ensure that only one version of React
is included in the output. Here is an example code:
This handling method also applies to projects with duplicate packages caused by multiple instances of peerDependencies in pnpm workspace
. The project directory structure is as follows:
In this project, when executing webpack build
under apps/web
, the code in the web
directory will be resolved to axios@0.27.2_debug@4.3.4
, and then the code in the packages/component
directory will be resolved to axios@0.27.2
.
Although they are the same version, they have different paths, resulting in two copies of axios
in the output.
The solution is to configure the web
project to only resolve the axios
package under the web
directory's node_modules
.