blob: 582e5cb3b32f231ce0de227daf60a71c1d683174 [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`.
> Note that as of Bazel 0.26, and with the recommended `managed_directories` attribute on the `workspace` rule in `/WORKSPACE`,
> the Bazel-managed `node_modules` directory is placed in your workspace root in the standard location used by npm or yarn.
## 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.
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"]},
)
```
As of Bazel 0.26 this feature is still experimental, so also add this line to the `.bazelrc` to opt-in:
```
common --experimental_allow_incremental_repository_updates
```
### 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 such as `jasmine_node_test`, fine grained
npm dependencies are specified in the `deps` attribute:
```python
jasmine_node_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",
managed_directories = {
"@app1_npm": ["app1/node_modules"],
"@app2_npm": ["app2/node_modules"],
},
)
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 self-managed dependencies
If you'd like to have Bazel use the `node_modules` directory you are managing,
then next you will create a `BUILD.bazel` file in your project root containing:
```python
package(default_visibility = ["//visibility:public"])
filegroup(
name = "node_modules",
srcs = glob(
include = ["node_modules/**/*"],
exclude = [
# Files under test & docs may contain file names that
# are not legal Bazel labels (e.g.,
# node_modules/ecstatic/test/public/中文/檔案.html)
"node_modules/test/**",
"node_modules/docs/**",
# Files with spaces are not allowed in Bazel runfiles
# See https://github.com/bazelbuild/bazel/issues/4327
"node_modules/**/* */**",
"node_modules/**/* *",
],
),
)
```
The example in `examples/user_managed_deps` uses self-managed dependencies.
To use the Yarn package manager, which we recommend for its built-in
verification command, you can run:
```sh
$ bazel run @nodejs//:yarn_node_repositories
```
If you use npm instead, run:
```sh
$ bazel run @nodejs//:npm_node_repositories install
```
The `@nodejs//:yarn_node_repositories` and `@nodejs//:npm_node_repositories` targets will run yarn/npm on all of the
package.json contexts listed `package_json` attribute of the `node_repositories`
repository rule in your WORKSPACE file (`node_repositories(package_json = [...])`).
If there are multiple package.json contexts in this rule but you would like to
run the bazel managed yarn or npm on a single context this can be done
using the following targets:
```sh
$ bazel run @nodejs//:yarn -- <arguments passed to yarn>
```
If you use npm instead, run:
```sh
$ bazel run @nodejs//:npm -- <arguments passed to npm>
```
This will run yarn/npm in the current working directory. To add a package with the `yarn add` command,
for example, you would use:
```sh
$ bazel run @nodejs//: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