Circle CI | Bazel CI |
---|---|
This is an alpha-quality release. Breaking changes are likely.
The nodejs rules integrate NodeJS development and runtime with bazel.
First, install a current bazel distribution, following the bazel instructions.
Create a BUILD.bazel
file in your project root:
package(default_visibility = ["//visibility:public"])
# NOTE: this will move to node_modules/BUILD in a later release
filegroup(name = "node_modules", srcs = glob(["node_modules/**/*"]))
Next create a WORKSPACE
file in your project root (or edit the existing one)
containing:
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
git_repository(
name = "build_bazel_rules_nodejs",
remote = "https://github.com/bazelbuild/rules_nodejs.git",
tag = "0.1.0", # check for the latest tag when you install
)
load("@build_bazel_rules_nodejs//:defs.bzl", "node_repositories")
# NOTE: this rule installs nodejs, npm, and yarn, but does NOT install
# your npm dependencies. You must still run the package manager.
node_repositories(package_json = ["//:package.json"])
You must now run the package manager to install the npm dependencies.
We recommend using the Yarn package manager, because it has a built-in command
to verify the integrity of your node_modules
directory.
You can run the version Bazel has already installed:
$ bazel run @yarn//:yarn
If you use npm instead, run:
$ bazel run @nodejs//:npm install
The nodejs_binary
rule allows you to run an application by giving the entry point.
The entry point can come from an external dependency installed by the package manager,
or it can be a .js
file from a package built by Bazel.
nodejs_test
is the same as nodejs_binary, but instead of calling it with bazel run
,
you call it with bazel test
. The test passes if the program exits with a zero exit code.
The jasmine_node_test
rule allows you to write a test that executes in NodeJS.
If you have installed the rollup package, you could write this rule:
load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary")
nodejs_binary(
name = "rollup",
entry_point = "rollup/bin/rollup",
)
and run it with
$ bazel run :rollup -- --help
See the examples/rollup
directory in this repository.
We can reference a path in the local workspace to run a program we write.
load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary")
nodejs_binary(
name = "example",
data = [
"@//:node_modules",
"main.js",
],
entry_point = "workspace_name/main.js",
args = ["--node_options=--expose-gc"],
)
This example illustrates how to pass arguments to nodejs (as opposed to passing arguments to the program).
The data
attribute is optional, by default it includes the node_modules
directory. To include your own
sources, include a file or target that produces JavaScript.
See the examples/program
directory in this repository.
The jasmine_node_test
rule can be used to run unit tests in NodeJS, using the Jasmine framework.
Targets declared with this rule can be run with bazel test
.
Attributes:
The srcs
of a jasmine_node_test
should include the test .js
files.
The deps
should include the production .js
sources, or other rules which produce .js
files, such as TypeScript.
The examples/program/index.spec.js
file illustrates this. Another usage is in https://github.com/angular/tsickle/blob/master/test/BUILD
Most bazel rules include package management. That is, the WORKSPACE
file installs your dependencies as well as the toolchain. In some environments, this is the normal workflow, for example in Java, Gradle and Maven are each both a build tool and a package manager.
In nodejs, there are a variety of package managers and build tools which can interoperate. Also, there is a well-known package installation location (node_modules
directory in your project). Command-line and other tools look in this directory to find packages. So we must either download packages twice (risking version skew between them) or point all tools to Bazel's external
directory with NODE_PATH
which would be very inconvenient.
Instead, our philosophy is: in the NodeJS ecosystem, Bazel is only a build tool. It is up to the user to install packages into their node_modules
directory, though the build tool can verify the contents.
Bazel generally guarantees builds are correct with respect to their inputs. For example, this means that given the same source tree, you can re-build the same artifacts as an earlier release of your program. In the nodejs rules, Bazel is not the package manager, so some reponsibility falls to the developer to avoid builds that use the wrong dependencies. This problem exists with any build system in the JavaScript ecosystem.
Both NPM and Yarn have a lockfile, which ensures that dependencies only change when the lockfile changes. Users are strongly encouraged to use the locking mechanism in their package manager.
References:
- npm: https://docs.npmjs.com/files/package-lock.json
- yarn: https://yarnpkg.com/lang/en/docs/yarn-lock/
Note that bazel-contrib#1 will take the guarantee further: by using the lockfile as an input to Bazel, the nodejs rules can verify the integrity of the dependencies. This would make it impossible for a build to be non-reproducible, so long as you have the same lockfile.