Our goal at Bit is to make bit install
work for everything and everyone out of the box. However, the JavaScript ecosystem is huge and sometimes some tweaks are required to make your tooling work properly. In this post, I will write about a few configuration settings that can change how bit installs your dependencies.
In case you prefer to watch rather than read, you can see me talking about Bit dependency resolver settings here:
When you use Bit, you can choose between two package managers: pnpm and Yarn. By default, when you create a new workspace, pnpm is set as the package manager.
/**
* main configuration for component dependency resolution.
**/
"teambit.dependencies/dependency-resolver": {
/**
* choose the package manager for Bit to use. you can choose between 'yarn', 'pnpm'
*/
"packageManager": "teambit.dependencies/pnpm",
"policy": {
"dependencies": {},
"peerDependencies": {}
}
},
To change the package manager to Yarn, just modify the packageManager
field to teambit.dependencies/yarn
, remove the node_modules
directory, and run bit install
.
Both pnpm and Yarn have two modes for linking the node_modules directory: isolated and hoisted. This can be configured using the nodeLinker
setting. By default, pnpm uses the isolated linker while Yarn uses the hoisted one.
By default, Bit will use pnpm with the isolated linker. It is the optimal choice as the isolated linker creates a clean node_modules where only the direct dependencies of the workspace are in the root. For instance, if you have is-odd
in your dependencies, this is how your node_modules will look like:
node_modules
├── .pnpm
│ ├── is-odd@3.0.1
│ │ └── node_modules
│ │ ├── is-odd
│ │ └── is-number --> ../../is-number@6.0.0
│ └── is-number@6.0.0
│ └── node_modules
│ └── is-number
└── is-odd --> ./.pnpm/is-odd@3.0.1/node_modules/is-odd
As you can see, only is-odd
is in the root of node_modules, and even that is only a symlink to the sources which are inside the store (node_modules/.pnpm
). The dependency of is-odd
is not hoisted to the root node_modules but is symlinked instead to the dedicated node_modules directory of is-odd
. This is a clean structure where everything is well organized and each dependency has only access to its dependencies.
However, the isolated linker uses symlinks, and some tools in the JavaScript ecosystem don't work well with symlinks (for instance, React Native). For those cases, you may set the nodeLinker
setting to hoisted
:
/**
* main configuration for component dependency resolution.
**/
"teambit.dependencies/dependency-resolver": {
/**
* choose the package manager for Bit to use. you can choose between 'yarn', 'pnpm'
*/
"packageManager": "teambit.dependencies/pnpm",
"nodeLinker": "hoisted",
"policy": {
"dependencies": {
"is-odd": "3.0.1"
},
"peerDependencies": {}
}
},
With this setting, no symlinks will be used and node_modules will look like this:
node_modules
├── is-number
└── is-odd
As you can see, both is-number
and is-odd
are in the root of node_modules.
In some cases, you might want to change what dependencies are installed for your dependencies. Let's consider a few scenarios.
is-odd
has is-number@5.0.0
(as an exact version) in the dependencies but is-number
has a vulnerability in that version. In this case, you want to force is-number
with the security fix to be installed instead of v5.0.0.For these cases, you may use the overrides field in the dependency-resolver config. In the example below, any version is-number
is replaced with v5.0.1:
/**
* main configuration for component dependency resolution.
**/
"teambit.dependencies/dependency-resolver": {
/**
* choose the package manager for Bit to use. you can choose between 'yarn', 'pnpm'
*/
"packageManager": "teambit.dependencies/pnpm",
"nodeLinker": "hoisted",
"policy": {
"dependencies": {
"is-odd": "3.0.1"
},
"peerDependencies": {}
},
"overrides": {
"is-number": "5.0.1"
},
},
If you want to only replace is-number
in the dependencies of is-odd
, you can use the next override:
{
...
"teambit.dependencies/dependency-resolver": {
...
"overrides": {
"is-odd>is-number": "5.0.1"
},
},
...
}
Or you may also override only a specific version of is-number
. The next override will only replace is-number@5
:
{
...
"teambit.dependencies/dependency-resolver": {
...
"overrides": {
"is-number@5": "5.0.1"
},
},
...
}
If you want to replace is-number
with your fork, you may use the alias syntax in the override:
{
...
"teambit.dependencies/dependency-resolver": {
...
"overrides": {
"is-number@5": "npm:@my-fork/is-number@5.0.1"
},
},
...
}
So, overrides are very powerful and may help out in some scenarios. However, be aware that overrides work only during local development, so they will not have any effect when someone imports or installs your components.
As of now, Bit doesn't show peer dependency warnings, when pnpm is used. However, it is important to install any required peer dependencies of your dependencies, so we plan to turn these warnings on in the future. For now, you can opt-in to peer dependency warnings by setting peerDependencyRules
:
{
...
"teambit.dependencies/dependency-resolver": {
...
"peerDependencyRules": {
"allowAny": [],
"ignoreMissing": []
}
},
...
}
Now let's install a dependency that requires a peer dependency.
As you can see, ts-node
requires typescript
to be in the dependencies as well. The right way to fix this would be to install typescript v2.7 or newer. But if you know that in your case typescript is not needed, you may ignore this warning by adding typescript
to peerDependencyRules.ignoreMissing
:
{
...
"teambit.dependencies/dependency-resolver": {
...
"peerDependencyRules": {
"allowAny": [],
"ignoreMissing": ["typescript"]
}
},
...
}
ignoreMissing
may also ignore package by patterns, so, for instance, you may ignore all issues with babel as well, using:
{
...
"teambit.dependencies/dependency-resolver": {
...
"peerDependencyRules": {
"allowAny": [],
"ignoreMissing": ["typescript", "@babel/*"]
}
},
...
}
Now let's install an unsupported version of typescript
:
If you know for sure that the dependency you are installing works fine with the peer dependent, you may allow either any version of the peer or specific versions. The configuration below will tell the package manager to accept typescript v1 as a valid peer for any dependency:
{
...
"teambit.dependencies/dependency-resolver": {
...
"peerDependencyRules": {
"allowAny": [],
"ignoreMissing": ["typescript", "@babel/*"],
"allowedVersions": {
"typescript": "1"
},
}
},
...
}
But you can also ignore all warnings related to peer dependency version mismatches, using the peerDependencyRules.allowAny
field that also accepts patterns. The below configuration will allow any version of react
and any version of babel
to resolve any peer.
{
...
"teambit.dependencies/dependency-resolver": {
...
"peerDependencyRules": {
"allowAny": ["react", "@babel/*"],
"ignoreMissing": ["typescript", "@babel/*"],
"allowedVersions": {
"typescript": "1"
},
}
},
...
}
In most cases, bit install
will work with any stack and no configuration changes. But if you have issues with dependencies due to some buggy tools in the ecosystem, you should try setting nodeLinker
to hoisted
. If changing the nodeLinker
doesn't help, or you know exactly which packages cause issues, the overrides
field might help.
Bit handles most of the peer dependency issues out-of-the-box using environments. But some 3rd party non-component dependencies might also use peer dependencies. For those cases, peer dependency warnings are great for identifying issues. However, peer dependency warnings are sometimes too strict, so you can use the peerDependencyRules
field whenever some warnings should be muted.
These are not the only settings that you can set for the dependency resolver aspect. See these docs for all the settings.
Zoltan is the master of dependencies and packages at Bit and the maker of pnpm. He likes Tacos🌮.