syncpack
Manage multiple package.json files, such as in Lerna Monorepos and Yarn/Pnpm
Workspaces
![Maintainability](https://api.codeclimate.com/v1/badges/516439365fdd0e3c6526/maintainability)
Installation
npm install --save-dev syncpack
Breaking Changes
Version 9.0.0
required some breaking API changes to add support for a new
customTypes
feature, but they are very simple to make.
GitHub Action
As of May 2022 there is now a
Syncpack GitHub Action.
It is new and less stable than syncpack itself, but please give it a try and
give your feedback.
Commands
fix-mismatches
Ensure that multiple packages requiring the same dependency define the same
version, so that every package requires eg. react@16.4.2
, instead of a
combination of react@16.4.2
, react@0.15.9
, and react@16.0.0
.
See versionGroups
if you have advanced requirements.
Options
-s, --source [pattern] glob pattern for package.json files to read from
-f, --filter [pattern] only include dependencies whose name matches this regex
-t, --types <names> only include dependencies matching these types (eg. types=dev,prod,myCustomType)
-c, --config <path> path to a syncpack config file
-i, --indent [value] override indentation. defaults to " "
-h, --help display help for command
Examples
syncpack fix-mismatches
syncpack fix-mismatches --source "apps/*/package.json"
syncpack fix-mismatches --source "apps/*/package.json" --source "core/*/package.json"
syncpack fix-mismatches --filter "typescript|tslint"
syncpack fix-mismatches --types dev
syncpack fix-mismatches --types dev,peer
syncpack fix-mismatches --indent " "
format
Organise package.json files according to a conventional format, where fields
appear in a predictable order and nested fields are ordered alphabetically.
Shorthand properties are used where available, such as the "repository"
and
"bugs"
fields.
Options
-s, --source [pattern] glob pattern for package.json files to read from
-c, --config <path> path to a syncpack config file
-i, --indent [value] override indentation. defaults to " "
-h, --help display help for command
Examples
syncpack format
syncpack format --source "apps/*/package.json"
syncpack format --source "apps/*/package.json" --source "core/*/package.json"
syncpack format --indent " "
lint-semver-ranges
Check whether dependency versions used within "dependencies", "devDependencies",
and "peerDependencies" follow a consistent format.
See semverGroups
if you have advanced requirements.
Options
-s, --source [pattern] glob pattern for package.json files to read from
-f, --filter [pattern] only include dependencies whose name matches this regex
-r, --semver-range <range> see supported ranges below. defaults to ""
-c, --config <path> path to a syncpack config file
-t, --types <names> only include dependencies matching these types (eg. types=dev,prod,myCustomType)
-h, --help display help for command
Examples
syncpack lint-semver-ranges
syncpack lint-semver-ranges --source "apps/*/package.json"
syncpack lint-semver-ranges --source "apps/*/package.json" --source "core/*/package.json"
syncpack lint-semver-ranges --filter "typescript|tslint"
syncpack lint-semver-ranges --semver-range ~
syncpack lint-semver-ranges --types dev --semver-range ~
syncpack lint-semver-ranges --types dev,peer semver-range ~
list
List all dependencies required by your packages.
Options
-s, --source [pattern] glob pattern for package.json files to read from
-f, --filter [pattern] only include dependencies whose name matches this regex
-c, --config <path> path to a syncpack config file
-t, --types <names> only include dependencies matching these types (eg. types=dev,prod,myCustomType)
-h, --help display help for command
Examples
syncpack list
syncpack list --source "apps/*/package.json"
syncpack list --source "apps/*/package.json" --source "core/*/package.json"
syncpack list --filter "typescript|tslint"
syncpack list --types dev
syncpack list --types dev,peer
list-mismatches
List dependencies which are required by multiple packages, where the version is
not the same across every package.
See versionGroups
if you have advanced requirements.
Options
-s, --source [pattern] glob pattern for package.json files to read from
-f, --filter [pattern] only include dependencies whose name matches this regex
-c, --config <path> path to a syncpack config file
-t, --types <names> only include dependencies matching these types (eg. types=dev,prod,myCustomType)
-h, --help display help for command
Examples
syncpack list-mismatches
syncpack list-mismatches --source "apps/*/package.json"
syncpack list-mismatches --source "apps/*/package.json" --source "core/*/package.json"
syncpack list-mismatches --filter "typescript|tslint"
syncpack list-mismatches --types dev
syncpack list-mismatches --types dev,peer
set-semver-ranges
Ensure dependency versions used within "dependencies"
, "devDependencies"
,
and "peerDependencies"
follow a consistent format.
See semverGroups
if you have advanced requirements.
Options
-s, --source [pattern] glob pattern for package.json files to read from
-f, --filter [pattern] only include dependencies whose name matches this regex
-c, --config <path> path to a syncpack config file
-r, --semver-range <range> see supported ranges below. defaults to ""
-t, --types <names> only include dependencies matching these types (eg. types=dev,prod,myCustomType)
-i, --indent [value] override indentation. defaults to " "
-h, --help display help for command
Examples
syncpack set-semver-ranges
syncpack set-semver-ranges --source "apps/*/package.json"
syncpack set-semver-ranges --source "apps/*/package.json" --source "core/*/package.json"
syncpack set-semver-ranges --filter "typescript|tslint"
syncpack set-semver-ranges --semver-range ~
syncpack set-semver-ranges --types dev --semver-range ~
syncpack set-semver-ranges --types dev,peer --semver-range ~
syncpack set-semver-ranges --indent " "
Configuration File
Creating a configuration file is optional, syncpack will search up the directory
tree in the following places:
- a
syncpack
property in package.json
- a
.syncpackrc
file in JSON or YAML format - a
.syncpackrc.json
, .syncpackrc.yaml
, .syncpackrc.yml
, .syncpackrc.js
,
or .syncpackrc.cjs
file - a
syncpack.config.js
or syncpack.config.cjs
CommonJS module exporting an
object - a
config.syncpack
property in package.json
If you want to specify a path to a configuration file, overriding the discovered
configuration file (if present), you can use the --config
option.
Default Configuration
{
"dependencyTypes": [
"dev",
"overrides",
"peer",
"pnpmOverrides",
"prod",
"resolutions",
"workspace"
],
"filter": ".",
"indent": " ",
"semverGroups": [],
"semverRange": "",
"sortAz": [
"contributors",
"dependencies",
"devDependencies",
"keywords",
"peerDependencies",
"resolutions",
"scripts"
],
"sortFirst": ["name", "description", "version", "author"],
"source": [],
"versionGroups": []
}
Configuration Options
--type
/ dependencyTypes
All of the properties in the table below are searched by default, but can be
disabled via the dependencyTypes
property of your config file or the --types
option on the command line.
- If
dependencyTypes
is unset or is an empty array, nothing is disabled. - If
dependencyTypes
is set, only those listed will be enabled. - If
--types
is set, it takes precedence and only its values will be used.
In this example, only dependencies found in dependencies
and devDependencies
will be inspected by syncpack.
{
"dependencyTypes": ["dev", "prod"]
}
Value | Property in package.json |
---|
dev | .devDependencies |
overrides | .overrides |
peer | .peerDependencies |
pnpmOverrides | .pnpm.overrides |
prod | .dependencies |
resolutions | .resolutions |
workspace | .version |
This list can be extended with your own customTypes
, so you can find and fix
versions found in other parts of your package.json files.
The workspace
type
This option synchronises the versions of your dependencies with the version
properties of the package.json files developed in your own local
workspace/project, when they relate to eachother.
Take this example, @your-repo/fetch
is developed in your repo:
{
"name": "@your-repo/fetch",
"version": "1.0.2"
// ...rest of the file
}
and another package developed in your repo depends on it:
{
"name": "@your-repo/ui",
// ...other stuff
"dependencies": {
"@your-repo/fetch": "0.9.4"
}
// ...rest of the file
}
When workspace
is enabled, syncpack will fix @your-repo/ui
so it depends on
version 1.0.2
of @your-repo/fetch
.
indent
The character(s) to be used to indent your package.json files when writing to
disk.
semverRange
Defaulted to ""
to ensure that exact dependency versions are used instead of
loose ranges, but this can be overridden in your config file or via the
--semver-range
command line option.
Supported Ranges
< <1.4.2
<= <=1.4.2
"" 1.4.2
~ ~1.4.2
^ ^1.4.2
>= >=1.4.2
> >1.4.2
* *
sortAz
When using the format
command, determines which fields within package.json
files should be sorted alphabetically. When the value is an Object, its keys are
sorted alphabetically. When the value is an Array, its values are sorted
alphabetically. There is no equivalent CLI Option for this configuration.
sortFirst
When using the format
command, determines which fields within package.json
files should appear at the top, and in what order. There is no equivalent CLI
Option for this configuration.
source
Defaults to ["package.json", "packages/*/package.json"]
to match most Projects
using Lerna or Yarn Workspaces, but this can be overridden in your config file
or via multiple --source
command line options. Supports any patterns supported
by glob.
customTypes
Extend syncpack to find and fix versions in your packages which are not
available by default. Custom types behave like any other dependency, so can be
included in versionGroups or semverGroups
etc.
The example below adds support for synchronising versions found in:
- The
engines
object. - The
packageManager
string.
{
"customTypes": {
"engines": {
"path": "engines",
"strategy": "versionsByName"
},
"packageManager": {
"path": "packageManager",
"strategy": "name@version"
}
}
}
customTypes[name]
The key of each custom type is its name, this can be used in the following
places to toggle when it is enabled:
--type
/ dependencyTypes
.versionGroup.dependencyTypes
semverGroup.dependencyTypes
customTypes[name].path
Where the version can be found in each package.json file, such as engines
,
packageManager
or some.nested.property
.
customTypes[name].strategy
A strategy defines how syncpack needs to read and write dependency names and
versions, there are 3 to choose from:
Name | Example |
---|
name@version | pnpm@7.27.0 |
version | 12.4.2 |
versionsByName | {"pnpm":"7.27.0", "semver": "7.3.8"} |
versionGroups
The most common use case for version groups is when some of the packages in your
Monorepo are considered alpha (or legacy). Since those packages are much further
ahead (or behind) the other packages, the dependencies within those packages
need to be managed differently to the rest of the Monorepo.
Your alpha packages might use unstable versions of some dependencies, while the
rest of the repo might need to remain on stable versions.
You don't want mismatches within your alpha packages, you don't want mismatches
within the other packages, but you do want those groups to use different
versions to each other and not have syncpack
make them all the same.
In the following example, 2 of our packages are using different versions of
react
and react-dom
to the rest of the project.
{
"versionGroups": [
{
"dependencies": ["react", "react-dom"],
"packages": ["@alpha/server", "@alpha/ui"]
}
]
}
👋 The dependencies
and packages
fields are processed using
minimatch, so the above example can
also be written as "packages": ["@alpha/**"]
.
syncpack
will make ensure that:
- The versions of
react
and react-dom
are the same within @alpha/server
and @alpha/ui
. - The versions of
react
and react-dom
are the same across every package
except @alpha/server
and @alpha/ui
. - The versions of
react
and react-dom
within @alpha/server
and @alpha/ui
can be different to the other packages in the monorepo. - The versions of every other dependency in the monorepo (eg
lodash
) are the
same across every package including @alpha/server
and @alpha/ui
.
Each dependency can only belong to one version group, the first rule which
matches a given dependency and package will apply.
You can be quite granular with these rules, so the partitioning doesn't have
to apply to an entire package:
- A specific dependency in a specific package.
- A specific dependency in some specific packages only.
- Any dependency who name matches a pattern such as
@aws-sdk/**
.
See semverGroups
for more examples, they work the same way.
versionGroup.dependencies
Required. An array of minimatch glob patterns which should match the key of
dependencies defined in your package.json files.
Pattern | Matches |
---|
["**"] | Any dependency |
["@aws-sdk/**"] | Any dependency with the scope @aws-sdk |
["react", "react-dom"] | Specific dependencies by name |
versionGroup.packages
Required. An array of minimatch glob patterns which should match the name
property of packages developed within your monorepo.
Pattern | Matches |
---|
["**"] | Any package |
["@my-repo/**"] | Any package with the scope @my-repo |
["my-server", "my-client"] | Specific packages by name |
versionGroup.dependencyTypes
Optional. If set, will result in only the dependency types included in that
array being considered a match for this version group.
In this example we define that all dependencies within peerDependencies
in the
repo must match, regardless of what versions of the same dependencies might be
used in dependencies
or devDependencies
.
{
"versionGroups": [
{
"dependencies": ["**"],
"dependencyTypes": ["peerDependencies"],
"packages": ["**"]
}
]
}
versionGroup.isBanned
Remove dependencies which you've decided should never be allowed.
{
"versionGroups": [
{
"dependencies": ["never-gonna"],
"isBanned": true,
"packages": ["**"]
}
]
}
versionGroup.isIgnored
Have syncpack ignore these dependencies completely.
{
"versionGroups": [
{
"dependencies": ["**"],
"isIgnored": true,
"packages": ["oops-moment", "workaround"]
}
]
}
versionGroup.pinVersion
Pin the version of all dependencies in this group to match this specific version
you've defined.
{
"versionGroups": [
{
"dependencies": ["@aws-sdk/**"],
"packages": ["**"],
"pinVersion": "3.55.0"
}
]
}
semverGroups
Allow some packages to have different semver range rules to the rest of your
monorepo. Each dependency can only belong to one semver group, the first rule
which matches a given dependency and package will apply.
Example use cases
1: Every dependency of @myrepo/library
should have a semver range of ~
,
regardless of what the rest of the monorepo uses:
{
"semverGroups": [
{
"range": "~",
"dependencies": ["**"],
"packages": ["@myrepo/library"]
}
]
}
2: Every dependency of @myrepo/library
whose name matches @alpha/**
should
have a semver range of ^
, regardless of what the rest of that package or the
rest of the monorepo uses:
{
"semverGroups": [
{
"range": "^",
"dependencies": ["@alpha/**"],
"packages": ["@myrepo/library"]
}
]
}
3: Every dependency in the monorepo whose name matches @alpha/**
should have a
semver range of ~
, regardless of what the rest of the monorepo uses:
{
"semverGroups": [
{
"range": "~",
"dependencies": ["@alpha/**"],
"packages": ["**"]
}
]
}
3: Production dependencies should have fixed version numbers, but development
and peer dependencies can be broader.
{
"semverGroups": [
{
"range": "",
"dependencyTypes": [
"dependencies",
"resolutions",
"overrides",
"pnpmOverrides",
"workspace"
],
"dependencies": ["**"],
"packages": ["**"]
},
{
"range": "~",
"dependencyTypes": ["devDependencies"],
"dependencies": ["**"],
"packages": ["**"]
},
{
"range": "^",
"dependencyTypes": ["peerDependencies"],
"dependencies": ["**"],
"packages": ["**"]
}
]
}
semverGroup.range
Which of the Supported Ranges this group should use.
semverGroup.dependencies
Works the same as versionGroup.dependencies
.
semverGroup.isIgnored
Works the same as versionGroup.isIgnored
.
semverGroup.packages
Works the same as versionGroup.packages
.
semverGroup.dependencyTypes
Works the same as
versionGroup.dependencyTypes
.
filter
A string which will be passed to new RegExp()
to match against package names
that should be included.
⚠️ filter
was originally intended as a convenience to be used from the
command line to filter the output of syncpack list
, it is not recommended
to add this to your config file to manage your project more generally.
Instead use versionGroups
and/or
semverGroups
.
Resolving Packages
package.json files are resolved in this order of precendence:
- If
--source
glob patterns are
provided, use those. - If using Yarn Workspaces,
read
workspaces
from ./package.json
. - If using Lerna, read
packages
from
./lerna.json
. - If using Pnpm, read
packages
from
./pnpm-workspace.yaml
. - Default to
'package.json'
and 'packages/*/package.json'
.
👋 Always add quotes around your --source
patterns
[more info].
Getting Help
Get help with issues by creating a Bug Report or discuss ideas by opening a
Feature Request.
Other Projects
If you find my Open Source projects useful, please share them ❤️