Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

yarn-deduplicate

scinos983.3kApache-2.06.0.2TypeScript support: included

Deduplication tool for yarn.lock files

yarn, yarn.lock, lockfile, duplicated, package manager, dedupe, deduplicate

readme

yarn-deduplicate

Cleans up yarn.lock by removing duplicates.

Builds: Node.js CI

This package only works with Yarn v1. Yarn v2 supports package deduplication natively!

A duplicate package is when two dependencies are resolved to a different version, even when a single version matches the range specified in the dependencies. See the Deduplication strategies section for a few examples.

Installation

Install the package globally:

npm install -g yarn-deduplicate

or

yarn global add yarn-deduplicate

This package also works wth npx, so you don't need to install it. For example, to recreate the most common scenario below with npx, run:

npx yarn-deduplicate yarn.lock

Usage

The most common scenario is to run

yarn-deduplicate yarn.lock

This will use the default strategy to remove duplicated packages in yarn.lock.

If you do not specify the yarn.lock path, it defaults to yarn.lock.

Check all available options with:

yarn-deduplicate --help

Duplicated packages

yarn.lock contains a list of all the dependencies required by your project (including transitive dependencies), and the actual package version installed to satisfy those dependencies.

For the context of this project, a "duplicated package" is a package that appears on multiple nodes of the dependency tree with overlapping version ranges but resolved to different versions.

For example, imagine that your project directly depends on lodash and babel, and babel depends on lodash as well. Specifically, your project depends on lodash@^1.0.0 and babel depends on lodash@^1.1.0. Because how the resolution algorithm works in Yarn, you might end up with two different copies of lodash (for example, version 1.0.1 and 1.2.0) in your project, even when 1.2.0 will suffice to satisfy both requirements for lodash. That's a "duplicated package".

It is important to note that we do not consider duplicated packages when the version ranges don't overlap. For example, if your project depends on underscore@^1.0.0 and underscore@^2.0.0. Your project will end up with two versions of underscore, and yarn-deduplicate won't change that.

When using yarn-deduplicate remember that it will change your dependency tree. There are certain code paths that now will run with a different set of dependencies. It is highly recommended that you review each change to yarn.lock. If the change is too big, use the flag --packages to deduplicate them gradually.

Why is this necessary?

Yarn documentation seems to suggest this package shouldn't be necessary. For example, in https://classic.yarnpkg.com/en/docs/cli/dedupe/, it says

The dedupe command isn’t necessary. yarn install will already dedupe.

This is, however, not exactly true. There are cases where yarn will not deduplicate existing packages. For example, this scenario:

  • Install libA. It depends on libB ^1.1.0. At this point, the latest version of libB is 1.1.2, so it gets installed as a transitive dependency in your repo

  • After a few days, install libC. It also depends on libB ^1.1.0. But this time, the latest libB version is 1.1.3.

In the above scenario, you'll end up with `libB@1.1.2andlibB@1.1.3` in your repo.

Find more examples in:

Deduplication strategies

--strategy <strategy>

highest will try to use the highest installed version. For example, with the following yarn.lock:

library@^1.1.0:
  version "1.2.0"

library@^1.2.0:
  version "1.2.0"

library@^1.3.0:
  version "1.3.0"

It will deduplicate library@^1.1.0 and library@^1.2.0 to 1.3.0

fewer will try to minimize the number of installed versions by trying to deduplicate to the version that satisfies most of the ranges first. For example, with the following yarn.lock:

library@*:
  version "2.0.0"

library@>=1.1.0:
  version "3.0.0"

library@^1.2.0:
  version "1.2.0"

It will deduplicate library@* and library@>=1.1.0 to 1.2.0.

Note that this may cause some packages to be downgraded. Be sure to check the changelogs between all versions and understand the consequences of that downgrade. If unsure, don't use this strategy.

It is not recommended to use different strategies for different packages. There is no guarantee that the strategy will be honored in subsequent runs of yarn-deduplicate unless the same set of flags is specified again.

Progressive deduplication

--packages <package1> <package2> <packageN>

Receives a list of packages to deduplicate. It will ignore any other duplicated package not in the list. This option is recommended when the number of duplicated packages in yarn.lock is too big to be easily reviewed by a human. This will allow for a more controlled and progressive deduplication of yarn.lock.

--scopes <scope1> <scope2> <scopeN>

Receives a list of scopes to deduplicate. It will ignore any other duplicated package not in the list. This option is recommended when deduplicating a large number of inter-dependent packages from a single scope, such as @babel. This will allow for a more controlled and progressive deduplication of yarn.lock without specifying each package individually.

Excluding packages

--exclude <package1> <package2> <packageN

--exclude-scopes <scope1> <scope2> <scopeN>

With these commands you can exclude certain packages/scopes from the deduplication process. This is specially useful if you want to apply a different strategy for a scope, for example.

Pre-release versions

By default, yarn-deduplicate will only match pre-release versions if they share they share the same major, minor and patch versions (example: ^1.2.3-alpha.1 and 1.2.3-alpha.2 can be deduplicated, but ^1.2.3 and 1.2.4-alpha.1 can't). This matches the behaviour of semver.

To change this behaviour you can use the flag --includePrerelease. This will treat all pre-release versionas as if they were normal versions (^1.2.3 and 1.2.4-alpha.1 can be deduplicated).

Usage in CI

This tool can be used as part of a CI workflow. Adding the flag --fail will force the process to exit with status 1 if there are duplicated packages. Example:

# Print the list of duplicated packages and exit with status 1
yarn-deduplicate --list --fail

# Deduplicate yarn.lock and exit with status 1 if changes were required
yarn-deduplicate --fail

Migration guide

From 2.x to 3.x

In this version we have adopted variadic arguments from commander.js. These are the equivalent commands:

#Old
yarn-deduplicate --packages libA,libB
yarn-deduplicate --scopes @scopeA,@scopeB
yarn-deduplicate --exclude libA,libB

#New
yarn-deduplicate --packages libA libB
yarn-deduplicate --scopes @scopeA @scopeB
yarn-deduplicate --exclude libA libB

A consequence of this change is that if you were using one or more of the affected options ( --packages, --scopes or --exclude) and a custom path for yarn.lock, you need to use -- to "stop" package/scope/exclude parsing:

yarn-deduplicate --packages libA libB -- path/to/yarn.lock

From 0.x to 1.x

In this version we have renamed the project and refactored the CLI. These are the equivalent commands:

Installation

# Old
npm install -g yarn-tools

# New
npm install -g yarn-deduplicate

List duplicates

# Old
yarn-tools list-duplicates path/to/yarn.lock

# New
yarn-deduplicate --list path/to/yarn.lock

Deduplicate yarn.lock

# Old
yarn-tools fix-duplicates path/to/yarn.lock > tmp
mv tmp path/to/yarn.lock

# New
yarn-deduplicate path/to/yarn.lock

Limit packages to deduplicate yarn.lock

# Old
yarn-tools fix-duplicates path/to/yarn.lock package1 package2


# New
yarn-deduplicate --packages package1,package2 path/to/yarn.lock

License

Copyright (c) 2022 Sergio Cinos and others. Apache 2.0 licensed, see LICENSE.txt file.

changelog

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog and this project adheres to Semantic Versioning.

Unreleased

6.0.2 - 2023-05-11

Chores

  • Update dependency tslib to ^2.5.0 (#236)
  • Update dependency vm2 from 3.9.13 to 3.9.17 (#238)
  • Update dependency commander to v10 (#237)
  • Update dependency typescript to v5 (#239)
  • Update dependency typescript to ^4.9.5 (#235)
  • Update dependency semver to ^7.5.0 (#234)
  • Update dependency prettier to ^2.8.8 (#233)
  • Update dependency eslint-config-prettier to ^8.8.0 (#222)
  • Update dependency @types/jest to ^29.5.1 (#231)
  • Update babel monorepo (#232)
  • Update dependency release-it to ^15.10.3 (#220)
  • Update dependency eslint to ^8.38.0 (#217)
  • Update Yarn to v3.5.1 (#218)
  • Update dependency eslint-plugin-jest to ^27.2.1 (#216)
  • Update dependency commander to ^9.5.0 (#223)
  • Update babel monorepo to ^7.21.4 (#219)
  • Update jest monorepo to ^29.5.0 (#221)

6.0.1 - 2022-12-11

Added

  • Update REAMDE with explicit example of npx (#206) (thanks to @karlhorky)

Chores

  • Configure renovate to run deduplicate
  • Bump vm2 from 3.9.9 to 3.9.13 (#207)
  • Update babel monorepo (#211)
  • Update babel monorepo to ^7.19.1 (#189)
  • Update dependency @types/jest to v29 (#184)
  • Update dependency commander to ^9.4.1 (#198)
  • Update dependency eslint to ^8.24.0 (#193)
  • Update dependency eslint to ^8.28.0 (#202)
  • Update dependency eslint to ^8.29.0 (#213)
  • Update dependency eslint-plugin-jest to ^27.0.4 (#190)
  • Update dependency eslint-plugin-jest to ^27.1.5 (#199)
  • Update dependency eslint-plugin-jest to ^27.1.6 (#209)
  • Update dependency jest to ^29.0.3 (#188)
  • Update dependency prettier to ^2.8.1 (#214)
  • Update dependency release-it to ^15.5.1 (#185)
  • Update dependency semver to ^7.3.8 (#200)
  • Update dependency tslib to ^2.4.1 (#208)
  • Update dependency typescript to ^4.8.3 (#191)
  • Update dependency typescript to ^4.9.3 (#196)
  • Update dependency typescript to ^4.9.4 (#212)
  • Update jest monorepo (#197)
  • Update Yarn to v3.3.0 (#203)

6.0.0 - 2022-08-29

Breaking changes

  • Drop support for Node 12.x. Minimum supported version is 14.x
  • Drop support for Yarn >= 2. It was never supported because it just doesn't work, but now Yarn version is enforced in engines entry in package.json

Chores

  • Do not pack Yarn version, rely on corepack instead
  • Enable corepack on CI
  • Update dependency @babel/core to ^7.18.13 (#175)
  • Update dependency @types/jest to ^28.1.8 (#174)
  • Update dependency eslint to ^8.23.0 (#180)
  • Update dependency eslint-plugin-jest to v27 (#183)
  • Update dependency jest to v29 (#178)
  • Update dependency release-it to ^15.4.0 (#176)
  • Update dependency typescript to ^4.8.2 (#179)
  • Update Yarn to v3.2.3 (#177)

5.0.2 - 2022-08-15

Chores

  • Re-release 5.0.1, it's missing from NPM 🤷
  • Update Yarn to v3.2.2 (#170)
  • Update dependency @release-it/keep-a-changelog to ^3.1.0 (#172)
  • Update dependency commander to ^9.4.0 (#166)
  • Update dependency release-it to ^15.3.0 (#167)
  • Update dependency @types/semver to ^7.3.12 (#171)
  • Update babel monorepo to ^7.18.10 (#169)
  • Update jest monorepo (#164)
  • Update dependency eslint-plugin-jest to ^26.8.2 (#165)
  • Update dependency eslint to ^8.22.0 (#168)

5.0.1 - 2022-07-05

Chores

  • Update all transitive dependencies
  • Adds Node 18 to the test matix (#154)
  • Update Yarn to v3.2.1 (#148)
  • Update dependency release-it to v15 (#162)
  • Update dependency @release-it/keep-a-changelog to v3 (#161)
  • Update dependency prettier to ^2.7.1 (#160)
  • Update dependency release-it to ^14.14.3 (#157)
  • Update dependency commander to ^9.3.0 (#158)
  • Update dependency eslint-plugin-prettier to ^4.2.1 (#159)
  • Update dependency semver to ^7.3.7 (#114)
  • Update dependency tslib to ^2.4.0 (#149)
  • Update dependency eslint to ^8.19.0 (#150)
  • Update babel monorepo to ^7.18.6 (#156)
  • Update dependency eslint-plugin-jest to ^26.5.3 (#151)
  • Update dependency @tsconfig/node12 to ^1.0.11 (#155)
  • Update jest monorepo to v28 (major) (#152)
  • Update dependency typescript to ^4.7.4 (#153)

5.0.0 - 2022-04-23

Breaking

  • Migration to TypeScript. This is a breacking changes because previously we provided TypeScript types, and those have changed. If you were not using the exported types, this change should be transparent for you.

Chores

  • Configured which files goes into npm package
  • Updated dependencies

[4.0.0] - 2022-03-21

Breaking

  • Dropped support for Node 10

Added

  • Added --exclude-scopes flag to exclude scopes (thanks to @sventschui)
  • Documented --exclude flags in README.md
  • Added support for Node 16

Fixed

  • Fixed typo in documentation (thanks to @ChetanGoti)
  • Updated repository information in package.json
  • Do not publish test files

Chores

  • Updated dependencies
  • Rename some directories/files
  • Addded typescript 4.6.2 (via peer dependency)

3.1.0 - 2020-10-25

Chores

  • Updated dependency jest to 26.6.1
  • Updated yarn to 1.22.10 (via policy)
  • Updated eslint-config-prettier to 6.14.0
  • Updated prettier to 2.1.2
  • Updated eslint-plugin-jest to 24.1.0
  • Updated eslint to 7.12.0
  • Prettify and fix typos in README.md (thanks to @friederbluemle)

Added

  • Added flag to include pre-release versions in the deduplication process. (thanks to @marcodejongh)

3.0.0- 2020-10-29

Breaking

Variadic flags

Flags --packages, --scopes and --exclude don't support comma-separated values anymore (eg: --packages libA,libB). Instead, you can pass multiple values per flag (eg: --packages libA libB) or pass the flag multiple times (eg: --packages libA --packages libB).

If you use one of those flags and you want to specify a custom yarn.lock file, you need to use -- to separate the arg. Example --packages libA -- ../project/yarn.lock

Chores

  • Updated Commander to 6.1.0
  • Updated eslint to 7.7.0
  • Updated yarn to 1.22.5
  • Updated prettier to 2.1.1
  • Updated jest to 26.4.2
  • Updated eslint-plugin-jest to 23.20.0

2.1.0- 2020-07-10

Chores

  • Updated dependencies
  • Move from CircleCI to GitHub actions
  • Clean up and dedupe yarn.lock

Added

  • Option --scopes to limit changes to a list of scopes (thanks to @sgomes)
  • Improve documentation for --strategy (thanks to @KubaJastrz)
  • Clean up .npmignore (thanks to @bluelovers)

2.0.0- 2020-02-29

Breaks

  • Drop support for Node < 10

1.2.0- 2020-02-29

Edit: Do not use. It breaks Node 8 compatiblity. Use 2.0.0 instead

Added

  • TypeScript definitions (thanks to @bluelovers)
  • Info about the source of duplication to README
  • CLI option to exclude packages (thanks to @JacobBlomgren)
  • Updated a bunch of dependencies

1.1.1- 2019-02-03

Fixed

  • Fixed typos in doc and CLI (thanks to @ActuallyACat and @Alonski)
  • Moved yarn from peerDependencies to engines

1.1.0- 2018-12-22

Added

  • Option --fail to exit the process with an error if there are duplicated packages (thanks to @amark)
  • If the path to yarn.lock is not specified, use yarn.lock as the default (thanks to @Joge97)

1.0.5- 2018-12-15

Changed

  • Support for Node 6 (thanks @leipert)

Chores

  • Moved eslint and stricter to devDependencies (thanks @hawkrives)
  • Re-enabled disabled tests (thanks @amark)

1.0.4- 2018-12-12

Changed

  • Fixes parsing the strategy. Now specifying -s fewer actually does something! (thanks to @leipert)

Chores

  • Added eslint and prettier to keep the code consistent

1.0.3- 2018-11-22

Changed

  • Retain Windows end-of-line (thanks to @Shingyx)

1.0.2- 2018-11-02

Chores

  • Added homepage to package.json

1.0.1- 2018-11-02

Chores

  • Removed unused files (tests, local .history) from the npm package. Only *.js and text files are included now.

1.0.0- 2018-10-31

Breaking

  • Renamed project from yarn-tools to yarn-deduplicate
  • CLI unification (see Migration to 1.0 guide)
  • Save changes back to yarn.lock by default (thanks to @felipemsantana)

Added

  • Added an option to specify the strategy when deduping files
  • Support for packages without the @<version> part
  • Support for non-semver versions, like <package>@next

Changed

  • Do not change the order of integrity field (thanks to @lukebatchelor)

Chores

  • Added support to CircleCI (thanks to @lukebatchelor)
  • Moved repo from BitBucket to GitHub
  • Bumped @yarnpkg/lockfile to 1.1.0 (thanks to @lukebatchelor)
  • Added 'dedupe' as package keyword (thanks to @gfx)

0.4.1- 2018-06-05

Changed

  • Use yarn ^1.0.0 and converted it to a peerDependency (thanks to @bj00rn)
  • Remove deprecated preferGlobal in package.json (thanks to bjorn@binovi.se)

0.4.0- 2018-01-18

Added

  • Restrict the list of packages to de-dupe on the command line.
  • This file.
  • Added AUTHORS.
  • Cleaned Markdown files.