Understanding Component Dependencies

There are several types of dependencies a component might have. Bit tries to automate the process of managing them using a specific set of rules.

TL;DR

Bit does all of the dependency tree management tasks automatically, and according to the implementation of the files within a component. So a dependency graph will be modified when you modify the import and require statements within a component. A component’s dependency tree is constructed out of two main sections; Package Dependencies and Component Dependencies.

All dependency resolution is made locally when tagging a version of a component. The state of a component is set according to the state of the developer’s workspace, at the time of development. This ensures the immutability of components. Additionally, all component dependencies are packed alongside the component itself, and cached by the Scope that contains the component.

Package Dependencies

Just like any other node module, a Bit component can have package dependencies. There three types of package dependencies for a node module are:

  • Dependencies
  • devDependencies
  • peerDependencies

However, unlike a typical node project, Bit automates the process of defining package dependencies for a component. When you use Bit to track components in a project, Bit will read through the component’s implementation, and evaluate the code’s Abstract Syntax Tree (AST) to list all require (or import) packages. Afterward, it splits those packages into three categories; dependencies, devDependencies, and peerDependencies. Bit does it by leveraging the project’s root package.json and by the types of files that require the package.

Bit uses these simple rules to define the various types of package dependencies for a component:

  1. If the required package is configured in the root package.json of the project as a peerDependency, Bit will set it as a peerDependency.
  2. According to the specific file-type within the Component (implementation or a test-file) that requires the package:
  3. A package will be a devDependency if it is required by one of the test files of the component.
  4. A package will be a dependency will set for all packages required by implementation-files.

Bit will then generate a package.json file automatically for the component.

Package Dependencies Edge Cases and Gotchas

  • A package listed both as a peerDependency and a dependency or devDependency in the package.json, will be considered it as a peerDependency.
  • Package required by both test-file and implementation-file will be set as a dependency.

Modifying Component Dependencies

It is possible to modify an auto-generated package.json. However, Bit does limit the possible modifications, as it will re-validate all manual modifications. Bit does it to ensure a component dependency graph is still valid. This is done whenever a component is tagged, to ensure that the component has all its dependencies defined.

Remove dependency

Bit handles the removal of dependencies automatically. Delete the require or import statement from the component’s code, and on the next tag, when the dependency graph calculation happens, Bit will remove it automatically.
Know that if you manually remove a dependency, but it is still being required in the code, Bit will add the dependency to the package.json again.

Add dependency

The only way to add a new dependency to a component’s package.json is to have it required by any of the component’s files. This means that if you add a new dependency to it by either editing the package.json or using a package manager’s --save ability, Bit will override it during the tag operation. This is to ensure that a component’s dependency graph will not end up bloated with unused dependencies.
If you want to remove dependency from a component, ensure that the code does not import or require the specific dependency, and Bit will remove it automatically.

Modify dependency version

You can manually update a dependency version in the component’s package.json file.

Modify dependency type

You can manually move a dependency from any type to any type. For example, move a dependency from dependencies to peerDependencies.

Component Dependencies

Component dependencies are the result of Bit’s calculation of a component’s dependency graph from the files that a component requires to operate. Similar to how Bit figures out the package dependencies;

  1. Bit evaluates the AST of the component’s files.
  2. Then, with adherence to any pre-configured Custom Module Resolution, Bit will try to build a list of all files a component requires.
  3. Once Bit has the list of files, it will try to figure out if any of the files are tracked as components.
  4. If a required file is a part of a component, Bit will add it to the component’s bit.json as a component dependency.
  5. If a required file is not a part of a component, Bit will not be able to isolate the component and will prompt an error. You will then need to decide on the appropriate action, as described here.

Bit manages changes in the component dependency tree automatically, just like it does for the package dependencies. However, unlike package dependencies, if a component has component-dependencies set to it, then upon exporting it.

Custom Module Resolution

If you are using Custom Module Definition feature, such as:

You will need to configure Bit with a similar configuration so that it can find the correct dependencies. The steps to do so can be found here. This is because in your code you will use absolute paths, and not relative paths to require files. Bit assumes that requiring absolute paths means that you require a package and not a file. If you have custom module resolution defined for your workspace, Bit will be able to resolve file dependencies from absolute paths as well.

On Tag Dependency Resolution

Unlike traditional package managers, that resolve dependencies versions when installing them, Bit resolves it when a version of a component is being tagged. This ensures the immutability of the component version by logging the exact version of each dependency used. When installing a component, Bit will be able to recreate the exact same dependency tree, with the same versions, ensuring that the same component environment will be available for each component when imported to another location.

Auto-tagging Component Versions

When you write code modification to the implementation of one file causes changes in the behavior of dependent files. This concept takes effect when working with components in your project.
For example, you have two components in your workspace A and B. Component B depends on A. You have modified to component B, and want to tag a new version for it. Once you do that, you local component A will use the latest version of component B. This means that it’s dependency tree has modified (the version of B is now different).
When such case happens, Bit will notify you that component A needs to be auto tagged bit Bit, to update its dependency tree. On your next tag, Bit will also promote component A by a patch version, because one of its dependencies has been updated, and the dependency tree of component A needs to be updated as well.

By auto-tagging components, Bit ensures that all local changes made, and tested, in your workspace will be available to all developers and projects that use that component.

Importing Component with Dependencies

As there are different types of dependencies a component may have, there are two distinct flows that are available for them. You can choose to configure your preferred flow for your workspace.

Sourcing Components

This entire section is relevant only for the bit import action of sourcing a component. Components that are installed as node modules, using npm/yarn will work just like any other package you install to a project.

Installing Component’s Package Dependencies

If a component has package dependencies, Bit will call your package manager and tell it to install the package dependencies. You can configure which package manager Bit will call in your workspace’s bit.json.

Yarn Workspaces

If you are using Yarn, and Yarn Workspaces, you can configure it to Bit. Bit will then set each component as its workspace for Yarn.

Installing Component Dependencies

There are two options available to install component’s component dependencies. Bit can install them as regular package dependencies, or source them as well.

Component Dependencies as Node Modules

This is the default option of installing component dependencies. Bit will try and use your package manager to install an imported component’s component dependencies. If later you choose to source (bit import) any of the component dependencies, to access their source code, Bit will create link files, to ensure that both sourced components are using each other, and not the installed packages.
For example, you have imported component A, which depends on component B. Component B was installed as a package, by npm, so component A uses the packaged component B. Now you use bit import again, but this time to source component B. Bit will add a link file in component A’s node_modules directory which directs it to the source file of component B. This is so that if you make any changes to component B, it will affect the functionality of component A, as they depend on each other.

Sourcing Dependencies to a Workspace

You can choose to import all component dependencies as components, thus sourcing them to your project as well. When you choose this route, Bit will import the source file of each dependency to your project, and install their package dependencies as node modules, using a package manager.