blob: 5cfdedb9c8f5abb40bc9df66a9b2030798ea588c [file] [log] [blame] [view] [edit]
---
title: Dependencies
layout: default
toc: true
---
# Dependencies
Bazel works alongside your existing package manager, either npm or yarn.
You manage your `package.json` file, editing by hand or by running commands like `npm install` or `yarn add`.
The package manager will also write a lock file, indicating exact versions for all transitive dependencies, which keeps your build hermetic and reproducible.
Bazel will run the package manager when the `package.json` or `*lock.json` files change, but you can also run the package manager yourself.
## Bazel-managed vs self-managed dependencies
You have two options for managing your `node_modules` dependencies: Bazel-managed or self-managed.
With the Bazel-managed dependencies approach, Bazel is responsible for making sure that `node_modules` is
up to date with your `package[-lock].json` or `yarn.lock` files. This means Bazel will set it up when the
repository is first cloned, and rebuild it whenever it changes. With the `yarn_install` or `npm_install`
repository rules, Bazel will setup your `node_modules` for you in an external workspace named after the
repository rule. For example, a `yarn_install(name = "npm", ...)` will setup an external
workspace named `@npm` with the `node_modules` folder inside of it as well as generating targets for each
root npm package in `node_modules` for use as dependencies to other rules.
For Bazel to provide the strongest guarantees about reproducibility and the
fidelity of your build, it is recommended that you use Bazel-managed dependencies.
This approach also allows you to use the generated fine-grained npm package dependencies
which can significantly reduce the number of inputs to actions, making Bazel sand-boxing and
remote-execution faster if there are a large number of files under `node_modules`.
## Using Bazel-managed dependencies
To have Bazel manage its own copy of `node_modules`, which is useful to avoid
juggling multiple toolchains, you can add one of the following to your `WORKSPACE`
file:
Using Yarn (preferred):
```python
load("@build_bazel_rules_nodejs//:index.bzl", "yarn_install")
yarn_install(
name = "npm",
package_json = "//:package.json",
yarn_lock = "//:yarn.lock",
)
```
Using NPM:
```python
load("@build_bazel_rules_nodejs//:index.bzl", "npm_install")
npm_install(
name = "npm",
package_json = "//:package.json",
package_lock_json = "//:package-lock.json",
)
```
> If you don't need to pass any arguments to `node_repositories`,
you can skip calling that function. `yarn_install` and `npm_install` will do it by default.
### symlink_node_modules and managed_directories
**The `managed_directories` feature is being removed from Bazel.**
See https://github.com/bazelbuild/bazel/issues/15463.
Using `managed_directories` is not recommended.
Furthermore, as of rules_nodejs 5.0, `symlink_node_modules` defaults to `False`. We've found that the benefits of using
`symlink_node_modules`, which allows Bazel to use a `node_modules` directory
that is in the user workspace, do not outweigh the downsides of the repository
rule not defining all of their inputs and of having to re-run the repository rule
if the user's `node_modules` folder is deleted. On persistent CI machines, that
will delete the `node_modules` folder when cleaning the clone between jobs, the
repository rule will run for every job when `symlink_node_modules` is enabled.
With `symlink_node_modules` disabled, the repository rule will only re-run if
its inputs change between jobs.
The remaining documentation in this section is for legacy usage only.
Set `symlink_node_modules` to `True` to configure `npm_install` and/or
`yarn_install` to install `node_modules` inside the user workspace and have
Bazel use the `node_modules` folder in the user workspace for the build via a
symlink to the user's `node_modules` in the external repository it creates.
```python
load("@build_bazel_rules_nodejs//:index.bzl", "npm_install")
npm_install(
name = "npm",
package_json = "//:package.json",
package_lock_json = "//:package-lock.json",
symlink_node_modules = True,
)
```
You should now add the `@npm` workspace to the `managed_directories` option in
the `workspace` rule at the top of the file. This tells Bazel that the
`node_modules` directory is special and is managed by the package manager. Add
the `workspace` rule if it isn't already in your `/WORKSPACE` file.
```python
workspace(
name = "my_wksp",
managed_directories = {"@npm": ["node_modules"]},
)
```
### yarn_install vs. npm_install
`yarn_install` is the preferred rule for setting up Bazel-managed dependencies for a number of reasons:
* `yarn_install` will use the global yarn cache by default which will improve your build performance (this can be turned off with the `use_global_yarn_cache` attribute)
* npm has a known peer dependency hoisting issue that can lead to missing peer dependencies in some cases (see https://github.com/bazelbuild/rules_nodejs/issues/416)
### Fine-grained npm package dependencies
You can then reference individual npm packages in your `BUILD` rules via:
```python
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary")
nodejs_binary(
name = "bar",
data = [
"@npm//foo",
"@npm//baz",
]
...
)
```
In this case, the `bar` nodejs_binary depends only the `foo` and `baz` npm packages
and all of their transitive deps.
For other rules fine grained
npm dependencies are specified in the `deps` attribute:
```python
nodejs_test(
name = "test",
...
deps = [
"@npm//jasmine",
"@npm//foo",
"@npm//baz",
...
],
)
```
### Multiple sets of npm dependencies
If your workspace has multiple applications, each with their own `package.json`
and npm deps, `yarn_install` (or `npm_install`) can be called separately for
each.
```python
workspace(name = "my_wksp")
yarn_install(
name = "app1_npm",
package_json = "//app1:package.json",
yarn_lock = "//app1:yarn.lock",
)
yarn_install(
name = "app2_npm",
package_json = "//app2:package.json",
yarn_lock = "//app2:yarn.lock",
)
```
Your application would then reference its deps as (for example) `@app1_npm//lodash`, or `@app2_npm//jquery`.
### Fine-grained npm package nodejs_binary targets
If an npm package lists one or more `bin` entry points in its `package.json`,
`nodejs_binary` targets will be generated for these.
For example, the `protractor` package has two bin entries in its `package.json`:
```json
"bin": {
"protractor": "bin/protractor",
"webdriver-manager": "bin/webdriver-manager"
},
```
These will result in two generated `nodejs_binary` targets in the `@npm//protractor/bin`
package (if your npm deps workspace is `@npm`):
* `@npm//protractor/bin:protractor`
* `@npm//protractor/bin:webdriver-manager`
These targets can be used as executables for actions in custom rules or can
be run by Bazel directly. For example, you can run protractor with the
following:
```sh
$ bazel run @npm//protractor/bin:protractor
```
Note: These targets are in the `protractor/bin` package so they don't
conflict with the targets to use in deps[]. For example, `@npm//protractor:protractor`
is target to use in deps[] while `@npm//protractor/bin:protractor` is the binary target.
### Coarse node_modules dependencies
Using fine grained npm dependencies is recommended to minimize
the number of inputs to your rules. However, for backward compatibility
there are also filegroups defined by `yarn_install` and `npm_install`
that include all packages under `node_modules` and which can be used
with the `node_modules` attribute of nodejs rules.
* `@npm//:node_modules` includes all packages under `node_modules` as well as the `.bin` folder
```python
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary")
nodejs_binary(
name = "bar",
node_modules = "@npm//:node_modules",
)
```
## Using Bazel provisioned node, npm & yarn
To run the version of node fetched by Bazel which defined in your WORKSPACE you
can use:
```sh
$ bazel run @nodejs//:node -- <arguments passed to node>
```
For example,
```
$ bazel run @nodejs//:node -- --version
v16.12.0
```
This will run node in the current working directory.
To run the Bazel fetched npm and/or yarn you can use:
```sh
$ bazel run @yarn//:yarn -- <arguments passed to yarn>
$ bazel run @nodejs//:npm -- <arguments passed to npm>
```
This will run yarn/npm in the current working directory.
For example, to add a package with the `yarn add` command you would run:
```sh
$ bazel run @yarn//:yarn -- add <package>
```
Note: the arguments passed to `bazel run` after `--` are forwarded to the executable being run.
[bazel instructions]: https://docs.bazel.build/versions/master/install.html