Skip to main content

Handling multiple versions of the same peer dependency


Node package managers install multiple versions of the same package when modules used by the same application require different versions to function.

This behavior is wanted when packages are used as internal implementations of their dependent modules.

This behavior is unwanted when different modules communicate with each other using shared objects that are instantiated by an installed package (the dependency). If different versions of the same package create different object instances then the “means of communication” is broken. There is no single object to address, no single source of truth. This is especially crucial when working with modules that are used as “plugins” of another module (for example, Babel), or when working with components that are coordinated in runtime using a shared library (for example, React).

To solve this problem, peer dependencies were introduced to the NPM world.

Setting a dependency as a peer dependency ensures the package manager installs only a single package version (i.e, disables the package manager default behavior). If that is not possible, if there is no single “agreed upon” version for all modules, then an error will be thrown.

The problem#

Peer dependencies assume a single “hosting code”, a single application. A Bit workspace is not a single “host”. It may create multiple “hosts” as each "application" is generated by a different Bit environment (that is what gives us the freedom to author and explore components of all sorts of types in one single workspace). That means different "hosts", different environments, may have the same package defined as a peer dependency only with different versions.

The solution#

Configure the dependency-resolver to install the needed peer dependency in a directory set by the environment. Then, resolve the path to that installed package for each component requiring it.

The new dependency configurations, set by either of the solutions shown below, will be addressed by your Bit workspace but will not affect any non-Bit consuming project. This is due to the fact that a package.json, containing valid ‘peerDependencies’ will still be generated for each component, each generated package.

Implementation 1: Extend an environment to add new peer dependencies#

Extend the environment and customize its dependencies to set the needed package as a peer dependency of all components managed by it. In addition, set the resolveFromEnv property to true. This will make sure to use the package provided by the environment.

For example, to set 'enzyme@3.11.0' as a peer dependency of all components managed by the @teambit.react/react environment, we'll create an extension and customize it like so:

import { EnvsMain, EnvsAspect } from '@teambit/envs';import { ReactAspect, ReactMain } from '@teambit/react';
// Create a new dependency configuration objectconst newDependencies = {  peerDependencies: {    'enzyme': {      version: '^3.11.0',      resolveFromEnv: true    }}
export class CustomReactExtension {  constructor(private react: ReactMain) {}
  static dependencies: any = [EnvsAspect, ReactAspect];
  static async provider([envs, react]: [EnvsMain, ReactMain]) {
    const customReactEnv = react.compose([      // Use this environment transformer to merge the new dependency configuration      // with the environment's default ones      react.overrideDependencies(newDependencies);
    return new CustomReactExtension(react);  }}
import { CustomReactExtension } from './custom-react.extension'export { CustomReactExtension }export default CustomReactExtension

Implementation 2: Set relevant components with a new peer dependency config#

  1. Select the relevant components (using 'variants' in the workspace.json) and configure their peer depedencies with the resolveFromEnv set to true.

  2. Select the environment and configure the above dependency as a standard dependency (the dependency-resolver will make sure to install this package in the right place to avoid conflicts between multiple versions of the same package. See here for more information.)

For example:

  "teambit.workspace/variants": {    "components/react": {      "teambit.dependencies/dependency-resolver": {        "policy": {          "peerDependencies": {            "enzyme": {              "version": "^3.11.0",              "resolveFromEnv": true            }          }        }      },      "extensions/my-react-extension": {        "teambit.dependencies/dependency-resolver": {          "policy": {            "dependencies": {              "enzyme": "^3.11.0"            }          }        }      }    }