De-fragment the dependencies your monorepo, reducing the number of copies of in-range dependencies
This will sync all the versions in your monorepo to the same version, within the configured range.
Works with all package-managers (pnpm, yarn, npm, etc)
Run in the root of your monorepo
npx defrag
or debug with
DEBUG=defrag npx defrag
In GitHub Actions
ensure-no-divergence:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npx defrag
- run: git diff --exit-code
No need to install dependencies for your monorepo to have this verification
Example .defragrc.yaml
# When writing to package.json,
# remove the semver-range, pinning the dependencie
# to an exact version.
#
# Possible values:
# pinned ( default )
# patches ( e.g.: ~1.0.0 )
# minors ( e.g.: ^1.0.0 )
write-as: pinned
# This whole object is optional,
# but if your monorepo has public libraries
# where you *need* wider ranges, that can be configured here
#
# overrides is an array of objects
overrides:
# path may be either a single glob or an array of glabs
- path:
- packages/*/addon/package.json
- packages/*/*/package.json
# for both devDependencies and dependencies
# in addition to the 3 global options,
# pinned, patches, and minors, you may also
# specify `false` to disable version re-writing entirely
# (which may be useful for some packages that do
# complex multi-version specifiers)
devDependencies: pinned
dependencies: minors
# Default assumes every package follows semver (minors) and
# is 100% safe to upgrade within those ranges.
#
# in practice, this is only true if all dependencies
# sharing those listed here do not use private APIs.
#
# Due to private API usage (or relying on "undefined" behavior)
# we are more conservative with these ranges and will deal with
# them more manually.
update-range:
"~":
- ember-source
- ember-data
- "@ember-data/*"
overrides:
- path: packages/**/*
devDependencies: false
dependencies: false
The algorithm is this:
scan all workspaces' package.json,
find the dependencies, adding their versions to a list
for each workspace
for each dependency
re-assign an in-range version to the highest the monorepo was already using
- reduces lockfile size
- reduces duplicate depenedncies
- allows package managers that "hoist" dependencies to be likely more correct
Dedupe operates on the lockfile, only and defrag
gives you more control over what dedupes, based on ranges in a configured .defragrc.yaml
.
additionally, this tool gives the ability to pin
versions, whereas dedupe would use whatever resolved dependency version satisfies the pre-existing ^
range.
If a package.json using a version format that isn't actually a version (and not yet accounted for), this is likely a bug -- the desired behavior is to ignore invalid versions and opt them out of being changed by this tool.