There are a few ways to use Angular with Bazel. See https://bazelbuild.github.io/rules_nodejs/examples#angular for an overview of all the options.
This example demonstrates the Google-internal toolchain, which is fast but not very compatible with existing applications.
This example is a monorepo, meant to show many different features and integrations that we expect are generally useful for enterprise use cases.
ng
command to run build, serve, test, and e2esrc/todos
for a typical NgModule
compiled as a library for use in the application, using the ts_project
rule in the BUILD.bazel
file.src/lib
for a trivial example of a pure-TS library that's consumed in the application, using the ts_project
rule in the BUILD.bazel
file.src/material
where we collect the material modules we use.src/reducers
where we use the NgRx Store.ng serve --prod
<script>
tags. Modern browsers will load code in the ES2015 syntax, which is smaller and requires fewer polyfills. Older browsers will load ES5 syntax.This example is deployed at https://bazel.angular.io/example
You only need to install one build tool, and which one you choose typically depends on what kind of development you do most often.
If you‘re a frontend developer, you should install NodeJS and yarn. The package.json
file has an engines
section which indicates the range of NodeJS and yarn versions that you could use. You simply run yarn
commands shown below, and don’t need to install Bazel or any other dependencies.
If you‘re a full-stack developer, you might be using Bazel for your backend already. In this case, you should install Bazel following instructions at http://bazel.build. Also install ibazel
, which is a watch mode for Bazel not included in the standard distribution. See https://github.com/bazelbuild/bazel-watcher#installation. You should have a .bazelversion
file which will ensure Bazel prints an error if your Bazel version is not in the supported range. You simply run bazel
commands shown below, and don’t need to install NodeJS, yarn, or any other dependencies.
First we'll run the development server:
$ ng serve # or $ ibazel run //src:devserver
This runs in “watch mode”, which means it will watch any files that are inputs to the devserver
, and when they change it will ask Bazel to re-build them. When the re-build is finished, it will trigger a LiveReload in the browser.
This command prints a URL on the terminal. Open that page to see the demo app running. Now you can edit one of the source files (src/lib/file.ts
is an easy one to understand and see the effect). As soon as you save a change, the app should refresh in the browser with the new content. Our intent is that this time is less than two seconds, even for a large application.
Control-C twice to kill the devserver
.
We can also run all the unit tests:
$ ng test # or $ bazel test //src/...
Or run the end-to-end tests:
$ ng e2e # or $ bazel test //e2e/...
In this example, there is a unit test for the hello-world
component which uses the @npm//karma
and web_test_suite
rule. There are also protractor e2e tests for both the prodserver
and devserver
which use the protractor_web_test_suite
rule.
Note: Bazel will only re-run the tests whose inputs changed since the last run.
We can run the application in production mode, where the code has been bundled and optimized. This can be slower than the development mode, because any change requires re-optimizing the app. This example uses Rollup and Uglify, but other bundlers can be integrated with Bazel.
$ ng serve --prod # or $ bazel run //src:prodserver
The production bundle is code split and routes such as /
and /todos
are lazy loaded. Code splitting is handled by the rollup_bundle rule which now supports the new code splitting feature in rollup.
Note: code splitting is not supported in development mode yet so the //src:devserver
target does not serve a code split bundle. The dynamic import()
statements will resolve to modules that are served in the initial JS payload.
Having a local node_modules
folder setup by yarn
or npm
is not necessary when building this example with Bazel. This example makes use of Bazel managed npm dependencies (https://bazelbuild.github.io/rules_nodejs/dependencies.html) which means Bazel will setup the npm dependencies in your package.json
for you outside of your local workspace for use in the build.
However, you may still want to run yarn
or npm
to manually setup a local node_modules
folder for editor and tooling support.
We use the standard firebase deploy command.
Run yarn deploy
to release changes to bazel.angular.io.
We use Bazel‘s docker support to package up our production server for deployment. Each time the app changes, we’ll get a slim new docker layer with just the modified files, keeping the round-trip for deployment incremental and fast. This example is configured to run on Google Kubernetes Engine, so we can have an elastic pool of backend machines behind a load balancer. This setup is more expensive to operate than something like Firebase Functions where the backend code is spun up on-demand, but is also more adaptable to scenarios like backend servers that need to run other binaries on the machine.
The application is currently live at http://35.197.115.230/
To run it under docker:
$ bazel run src:nodejs_image -- --norun $ docker run --rm -p 8080:8080 bazel/src:nodejs_image
Deploy to production:
gcloud auth configure-docker
gcloud container clusters get-credentials angular-bazel-example --zone=us-west1-a
bazel run :deploy.create
bazel run :deploy.replace
Tips:
Run the binary without docker
$ bazel run src:nodejs_image.binary
What's in the image?
$ bazel build src:nodejs_image && file-roller dist/bin/src/nodejs_image-layer.tar
Tear down all running docker containers
$ docker rm -f $(docker ps -aq)
Hop into the running image on kubernetes
$ kubectl exec angular-bazel-example-prod-3285254973-ncv3g -it -- /bin/bash